nginxnext.js

NextJs: Nginx returning 404 on API routes


I'm having an issue here where my NextJs API is returning 404 not found when executed from getInitialProps during a page reload in production.

The error message from my PM2 logs were mentioning the 404 not found was returned by Nginx.

It seems like NGINX is not able to detect my routes in /api/*.

This issue doesn't happen on local and I'm suspecting it is an issue or configuration I have missed out in nginx.

Here are my current versions that I am using

  1. NextJs: 9.4.0
  2. nginx/1.18.0 (Ubuntu) - On AWS EC2

UPDATE

I was able to scope down the issue into a SSL problem where if i disable my SSL in my nginx.conf file. The APIs are working fine. However I am still not able to find a solution to this.

nginx config file

server {

  # Your domain
  server_name mydomain.com;

  # Proxy to nuxt renderer.
  location / {
    auth_basic "Restricted Content";
    auth_basic_user_file /etc/nginx/.htpasswd;
    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;
  }

  # Redirect from /path/ to /path
  rewrite ^/(.*)/$ /$1 permanent;

  listen 443 ssl; # managed by Certbot
  ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem; # managed by Certbot
  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = mydomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


  listen 80;
  server_name mydomain.com;
    return 404; # managed by Certbot

}

NextJs getInitialProps code

static async getInitialProps(context) {
    const isServer = !!context.req
    const { employerAccessToken } = nextCookie(context)

    api.setBaseURL('/')
    api.setAuthToken(employerAccessToken)
    if (isServer) {
        api.setCookie(nextCookie(context))
    }
    let apiResponse = await api.get('/api/employer/profile')

    if (!apiResponse.ok) {
        console.log('not OK');
        console.log(apiResponse);
        redirectToEmployerLogin(context)
        return {}
    }

    let wrappedProps = {}

    if (WrappedComponent.getInitialProps) {
        wrappedProps = await WrappedComponent.getInitialProps(context)
    }
}

api/employer/profile

const handler = async (req, res) => {
    if (req.method == 'GET') {
        try {
            const { employerAccessToken } = nextCookie({ req, res })

            api.setBaseURL(process.env.NEXT_PUBLIC_API_URL)
            api.setAuthToken(employerAccessToken)
            const apiResponse = await api.get('/employers/me', req.query)

            console.log('apiResponse in /api/employer/profile');
            console.log(apiResponse);

            res.status(apiResponse.status).json(apiResponse.data)
        } catch (error) {
            logger.error(`message - ${error.message}, stack trace - ${error.stack}`)
            res.status(500).json({})
        }
    }
}

PM2 Log Error enter image description here


Solution

  • SOLVED

    Apparently the issue is caused by nginx redirecting a server request from http to https however NextJs is not able to identify the route when its in https hence Nginx returns a 404 not found.

    The solution would be to allow a proxy pass for localhost to maintain request at port 80 instead of forwarding all port 80 request to 443.

    server_block conf

    # redirect http to https
    server {
      listen 80;
      listen [::]:80;
      server_name 127.0.0.1 localhost;
      
      location / {
              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_cache_bypass $http_upgrade;
          } 
    }
    
    server {
      listen 80 default_server;
      listen [::]:80 default_server;
      server_name yourserver.com www.yourserver.com;
      return 301 https://$server_name$request_uri;
    
    }
    
    server {
      # listen on *:443 -> ssl;
      listen 443 ssl default_server;
      listen [::]:443 ssl default_server;
    
      server_name yourserver.com;
      
      ssl_certificate /etc/letsencrypt/live/yourserver.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/yourserver.com/privkey.pem; # managed by Certbot
        ssl_session_cache builtin:1000 shared:SSL:10m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELIA:!DES:!MD5:!PSK:!RC4;
        ssl_prefer_server_ciphers on;
    
      location / {
        # reverse proxy for next server
        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_cache_bypass $http_upgrade;
      
        proxy_read_timeout 300s;
        proxy_connect_timeout 75s;
    
        # we need to remove this 404 handling
        # because next's _next folder and own handling
        # try_files $uri $uri/ =404;
      }
      
      location ~ /.well-known {
        allow all;
      }
    }