Updated
—
5 min read
Here's a quick and easy tutorial for setting up SSL for your NextJS app.
To host NextJS apps, I use Nginx server blocks and a reverse proxy.
Here's what this short tutorial will show you how to do:
Here's a basic set up for your Nginx server block you can copy.
# HTTP server block
server {
listen 80 default_server;
server_name example.com;
# Serve ACME challenge static files
location ~ /.well-known/acme-challenge {
root /var/www/certbot/example.com;
allow all;
}
# Redirect all other traffic to HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
# HTTPS server block
server {
listen 443 ssl;
server_name example.com;
# Uncomment the following lines after generating certificates
# ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Reverse proxy for NextJS app
location / {
proxy_pass http://127.0.0.1:3000; # Change this to your NextJS app port
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_read_timeout 90;
}
}
# HTTP server block for www.
server {
listen 80;
server_name www.example.com;
location ~ /.well-known/acme-challenge {
root /var/www/certbot/example.com;
allow all;
}
location / {
return 301 https://example.com$request_uri;
}
}
# Redirect www to non-www
server {
listen 443 ssl;
server_name www.example.com;
# Uncomment the following lines after generating certificates
# ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}
This configuration does the following:
Make sure to replace example.com
with your domain name and 3000
with your NextJS app port.
Then you can run:
sudo nginx -t
sudo systemctl reload nginx
To generate SSL certificates, Let's Encrypt needs to verify that you own the domain using challenges.
The simplest and easiest challenge is the HTTP-01 challenge, which requires you to serve a specific file in a specific location on your server.
But since we're using NextJS, we'd normally serve files in the public
directory, and this would mean rebuilding our NextJS app every time we need to renew our certificates.
Instead, we can direct the ACME challenge requests to a specific directory on our server.
cd /var/www # or any directory you prefer
mkdir certbot
cd certbot
mkdir example.com # make a separate directory for each domain
Looking back at our server block configuration, you can see that we set the root for the ACME challenge to /var/www/certbot/example.com
.
Now, any files placed in /var/www/certbot/example.com
will be served as static files by Nginx.
Using Let's Encrypt Certbot, we can now generate our SSL certificates.
Make sure certbot
is installed:
bashsudo apt update
sudo apt install certbot
Then run the following command to generate your certificates:
bashsudo certbot certonly --webroot -w /var/www/certbot/example.com -d example.com -d www.example.com
This example is generating certificates for both example.com
and www.example.com
.
After successfully generating our SSL certificates, we can update our server block:
# HTTP server block
server {
...
}
# HTTPS server block
server {
listen 443 ssl;
server_name example.com;
# Now you can uncomment the following lines after generating certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
...
}
# HTTP server block for www.
server {
...
}
# Redirect www to non-www
server {
listen 443 ssl;
server_name www.example.com;
# Now you can uncomment the following lines after generating certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}
And lastly run:
sudo nginx -t
sudo systemctl reload nginx
Lastly, we just want to add a cronjob to automatically renew our certificates.
crontab -e
Then add the following line below the commented section:
48 2,14 * * * certbot renew --quiet --post-hook "systemctl reload nginx"
This will run every day at 2:48 AM and 2:48 PM to check if your certificates need to be renewed. If they do, it will renew them and reload Nginx.
Note:
It's recommended to choose a random minute and hour to avoid overloading Let's Encrypt servers. So instead of
48 2,14
, you could choose something like13 3,15
or23 4,16
.
And that's it! You should now have SSL set up for your NextJS app.
I hope this short tutorial was helpful. In my early days of NextJS and self-hosting, I was (naively) using DNS challenges to generate certificates, which was a bit of a hassle.
Serving ACME challenges as static files is a much simpler and more efficient way to handle SSL certificates for your NextJS apps.
Meet the Author
Ryan Chiang
Hello, I'm Ryan. I build things and write about them. This is my blog of my learnings, tutorials, and whatever else I feel like writing about.
See what I'm building →.
Thanks for reading! If you want a heads up when I write a new blog post, you can subscribe below: