How to Fix Nginx 404 Not Found on Google Cloud Run


  1. The Root Cause On Google Cloud Run, an Nginx 404 often indicates that Nginx is running within the container but cannot locate the requested files at its configured root directory. This usually stems from a mismatch between Nginx’s root directive and the actual path where application static files or web content are placed within the Docker image during the build process.

  2. Quick Fix (CLI) After correcting your nginx.conf or Dockerfile locally, you need to rebuild and redeploy your service.

    # 1. Set variables for your project and service
    PROJECT_ID=$(gcloud config get-value project)
    SERVICE_NAME="your-cloud-run-service-name" # Replace with your Cloud Run service name
    REGION="us-central1" # Replace with your Cloud Run service region
    
    # 2. Build the new Docker image (assuming Dockerfile is in the current directory)
    IMAGE_NAME="gcr.io/${PROJECT_ID}/${SERVICE_NAME}:latest"
    docker build -t ${IMAGE_NAME} .
    
    # 3. Push the new image to Google Container Registry
    docker push ${IMAGE_NAME}
    
    # 4. Deploy the updated service on Cloud Run
    gcloud run deploy ${SERVICE_NAME} --image ${IMAGE_NAME} \
      --platform managed --region ${REGION} \
      --allow-unauthenticated # Add or remove --allow-unauthenticated as per your service's requirements
  3. Configuration Check The primary configuration to check is your nginx.conf file and your Dockerfile.

    • File to edit: nginx.conf (typically found at /etc/nginx/nginx.conf or /etc/nginx/conf.d/default.conf inside the container).

    • Key lines to change/verify:

      # In your 'server' block within nginx.conf
      server {
          listen ${PORT:-8080}; # Cloud Run injects the PORT environment variable; fall back to 8080
          root /app/public;     # <-- CRITICAL: This path MUST be the absolute path where your static files are
                                #     actually located inside the container. Common paths include /app, /app/build, or /usr/share/nginx/html.
          index index.html index.htm; # Specify your index file(s)
      
          location / {
              try_files $uri $uri/ =404; # Ensures Nginx correctly attempts to find files and serves a 404 if not found
          }
      
          # Add other necessary location blocks for API routes, etc., if any
          # For example, if you have a backend API running on another port in the same container:
          # location /api/ {
          #     proxy_pass http://localhost:8081; # Assuming your backend is on port 8081
          #     proxy_set_header Host $host;
          #     proxy_set_header X-Real-IP $remote_addr;
          # }
      }
    • Also check your Dockerfile: Ensure that your static assets are copied to the exact root directory specified in nginx.conf.

      # Example Dockerfile snippet
      # ...
      # Copy your custom Nginx configuration file
      COPY ./nginx.conf /etc/nginx/nginx.conf
      
      # Copy your static web content (e.g., built frontend assets)
      # The destination path MUST match the 'root' directive in your nginx.conf
      COPY ./dist /app/public # <-- This '/app/public' should match 'root /app/public;' in nginx.conf
      
      # Expose the port Nginx will listen on (often 8080 for Cloud Run)
      ENV PORT 8080
      EXPOSE ${PORT}
      
      # Command to start Nginx, ensuring it stays in the foreground
      CMD ["nginx", "-g", "daemon off;"]
  4. Verification After the deployment is complete, verify the fix by accessing your service URL.

    # 1. Obtain your Cloud Run service URL
    SERVICE_URL=$(gcloud run services describe ${SERVICE_NAME} \
      --platform managed --region ${REGION} \
      --format 'value(status.url)')
    
    # 2. Use curl to request the root path or a known static file
    curl -v "${SERVICE_URL}"
    # Alternatively, for a specific static file (e.g., index.html):
    # curl -v "${SERVICE_URL}/index.html"
    
    # Look for an HTTP/1.1 200 OK status code in the curl output.
    # A 404 response (or any other non-2xx status code) indicates the issue persists.