Home > OS >  Nginx Next.js 404 on static files
Nginx Next.js 404 on static files

Time:06-11

I have a WordPress site (in /var/www/html/ path) with an nginx server, now I want a specific page to be in nextjs (in /var/www/html/ path) by proxy, for port 3000. I'm requesting, example.com/specific-next-page The page does load, but all static files (js/css) are not found (404). for example:

GET https://example.com/_next/static/css/1ca183f3cca214b7.css net::ERR_ABORTED 404

This is what I see in nginx logs. 2022/06/10 14:47:25 [error] 3015605#3015605: *10 open() "/var/www/html/_next/static/BA1dIGPMYI7hn430ayZ2f/_buildManifest.js" failed (2: No such file or directory), client: 172.70.200.XXX, server: example.com, request: "GET /_next/static/BA1dIGPMYI7hn430ayZ2f/_buildManifest.js HTTP/2.0", host: "example.com", referrer: "https://example.com/specific-next-page"

I understand that the problem is that nginx is looking for the files in the /var/www/html folder which is the WordPress folder, and nextjs is in /var/www/next That is, the correct path for the static files is /var/www/next/.next/static

I really tried a lot of options and I failed. This is my nginx config file.

upstream php-handler-https {
  server 127.0.0.1:9000;
}

server {
  listen 443 ssl http2 ;
  listen [::]:443 ssl ;
  server_name example.com www.example.com; 
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot

  root /var/www/html/;
  index index.php;

  client_max_body_size 2G;
  fastcgi_buffers 64 4K;

  access_log /var/log/nginx/wordpress_https_access.log combined;
  error_log /var/log/nginx/wordpress_https_error.log;

  server_tokens off;

  location = /favicon.ico {
    log_not_found off;
    access_log off;
  }

  location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
  }

  # NEXT PROXY
  location /__nextjs_original-stack-frame {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
  location /content {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Port $server_port;
  }
  location /_next {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Port $server_port;
  }
 
  # specific-page - next.js
  location /specific-next-page {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Port $server_port;
  }
  # END NEXT


  location / {
    try_files $uri $uri/ /index.php?$args ;
  }

  # protected area (XHProf)
  location ^~ /xhprof/xhprof_html/ {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/htpasswd/xhprof;

    location ~ \.php(?:$|/) {
      fastcgi_split_path_info ^(. \.php)(/. )$;
      include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      fastcgi_param PHP_FLAG "session.auto_start=off \n mbstring.encoding_translation=off";
      fastcgi_param PHP_VALUE "assert.active=0 \n mbstring.http_input=pass \n mbstring.http_output=pass";
      fastcgi_pass php-handler-http ;
      fastcgi_read_timeout 60s;
    }
  }

  # protected area (phpmyadmin)
  location ^~ /mysqladmin/ {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/htpasswd/phpmyadmin;

    location ~ \.php(?:$|/) {
      fastcgi_split_path_info ^(. \.php)(/. )$;
      include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      fastcgi_param PHP_FLAG "session.auto_start=off \n mbstring.encoding_translation=off";
      fastcgi_param PHP_VALUE "assert.active=0 \n mbstring.http_input=pass \n mbstring.http_output=pass";
      fastcgi_pass php-handler-http ;
      fastcgi_read_timeout 60s;
    }
  }

  location ^~ /wp-admin/install.php {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/htpasswd/wpadmin;

    location ~* \.(htaccess|htpasswd) {
      deny all;
    }

    location ~ \.php(?:$|/) {
      fastcgi_split_path_info ^(. \.php)(/. )$;
      include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      fastcgi_param HTTPS on;
      fastcgi_param PHP_VALUE "auto_prepend_file=/var/www/html/xhprof/external/header.php";
      fastcgi_pass php-handler-https;
      fastcgi_read_timeout 60s;
    }
  }

  location ~* \.(htaccess|htpasswd) {
    deny all;
  }

  location ~* \.(?:ini|conf|txt)$ {
    deny all;
  }

  location ~ \.php(?:$|/) {
    fastcgi_split_path_info ^(. \.php)(/. )$;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param HTTPS on;
    fastcgi_param PHP_VALUE "auto_prepend_file=/var/www/html/xhprof/external/header.php";
    fastcgi_pass php-handler-https;
    fastcgi_read_timeout 60s;
  }

  # set long EXPIRES header on static assets
  location ~* \.(?:jpg|jpeg|gif|bmp|ico|png|css|js|swf)$ {
    expires 30d;
    access_log off;
  }
}

CodePudding user response:

If /var/www/html is the root of your web server, clients are not being served files from /var/www/next. The .next folder must be in a place served by nginx and available publicly to clients.

I suggest putting .next under /var/www/html

CodePudding user response:

Do you understand how nginx select a location for handling the request? Do you understand location priorities according to used modifier? Did you read location directive documentation at all? You already use the ^~ location modifier in you configuration. Do you understand what does it mean and how is it work?

Because of your regex location ~* \.(?:jpg|jpeg|gif|bmp|ico|png|css|js|swf)$ { ... } none of any requests for these file types will ever be handled by any prefix location without the ^~ modifier (well, at least until there will be another regex location inside the longest prefix one). The minimal fix for your configuration will be at least to use the ^~ modifier for the /_next prefix location:

location ^~ /_next {
    ...
}

However serving static files by nginx itself without proxying that requests to the app will be much more effective. To do it you can use the following location:

location ^~ /_next/static/ {
    alias /var/www/next/.next/static/;
    try_files $uri =404;
}

Generally, using any additional regex location that can be avoided is a performance penalty since it (most probably, if the request won't be captured by another prefix location with ^~ modifier or regex location before) will eliminate an expensive PCRE library call to perform a match against the regex pattern. These two locations:

location ~* \.(htaccess|htpasswd) {
    deny all;
}
location ~* \.(?:ini|conf|txt)$ {
    deny all;
}

can be easily combined into the single one:

location ~* \.(?:htaccess|htpasswd|ini|conf|txt)$ {
    deny all;
}

and if disabling assets requests logging is not essential for you, this one:

location ~* \.(?:jpg|jpeg|gif|bmp|ico|png|css|js|swf)$ {
    expires 30d;
    access_log off;
}

can be replaced with somewhat bigger yet much more effective configuration to adding cache policy according to the Content-Type HTTP response header (according to the default MIME types being used by nginx 1.21):

map $sent_http_content_type $expires {
    image/jpeg                     30d;
    image/gif                      30d;
    image/x-ms-bmp                 30d
    image/x-icon                   30d;
    image/png                      30d;
    text/css                       30d;
    application/javascript         30d;
    application/x-shockwave-flash  30d;
    default                        off;
}

server {
    ...
    location / {
        try_files $uri $uri/ /index.php?$args;
        expires $expires;
    }
    ...
}

Moreover, this way you will be able to easily add the same cache policy to your Next.js app, if needed (using the ^~ modifier won't be necessary anymore):

location /_next/static/ {
    alias /var/www/next/.next/static/;
    try_files $uri =404;
    expires $expires;
}

I described this method much more comprehensively here.


About the following lines in your configuration:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

These are used for the WebSocket protocol proxying and can break your app under some circumstances if WebSocket is not being used. I'm really doubt you need those lines for every Next.js related location. Read more technical details here.

  • Related