I'm hosting a project on a VPS using Docker. The project consists of:
A React frontend (Vite),
A .NET backend,
NGINX as a reverse proxy.
with a file structure:
project/
├── docker-compose.yml
├── example.client/
│ ├── dockerfile
│ └── build/
├── example.server/
│ ├── dockerfile
│ └── app/
└── nginx/
├── default.conf
├── nginx.conf
└── certs
├── api.example
│ ├── privkey.pem
│ └── fullchain.pem
└── example
├── privkey.pem
└── fullchain.pem
I want the frontend to work on and the backend on
. Both should be served over HTTPS using certificates from Let's Encrypt.
I've managed to:
.What I've succesfully achived is frontend to work when going to example and to present the contents of frontend, but then when a request is made, I see in a header address instead of
so the outcome is 504 after a timeout and it's driving me crazy. I believe there is something wrong with my nginx config, so here are all the configurations I've been using:
NGINX configuration:
server {
listen 80;
server_name example www.example;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example www.example;
ssl_certificate /etc/nginx/certs/example/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/example/privkey.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://frontend:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api/ {
proxy_pass /;
proxy_set_header Host api.example;
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;
proxy_connect_timeout 90;
proxy_send_timeout 90;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
server {
listen 80;
server_name api.example www.api.example;
return 301 https://$host$request_uri;
}
server {
listen 443;
server_name api.example;
ssl_certificate /etc/nginx/certs/api.example/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/api.example/privkey.pem;
location / {
proxy_pass :5001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
error_log /var/log/nginx/api_error.log;
access_log /var/log/nginx/api_access.log;
}
docker-compose.yml:
version: '3.9'
services:
backend:
image: example.server
container_name: example.server
build:
context: ./example.server
dockerfile: dockerfile
ports:
- "5000:5000"
- "5001:5001"
environment:
- ASPNETCORE_URLS=https://+:5001
- ASPNETCORE_Kestrel__Certificates__Default__Path=/certs/fullchain.pem
- ASPNETCORE_Kestrel__Certificates__Default__KeyPath=/certs/privkey.pem
volumes:
- /etc/letsencrypt/archive/api.example:/certs:ro
networks:
- app-network
frontend:
image: example.client
container_name: example.client
build:
context: ./example.client
dockerfile: dockerfile
ports:
- "3000:80"
depends_on:
- backend
networks:
- app-network
volumes:
- /etc/letsencrypt/archive/api.example:/etc/ssl/certs/api.example:ro
- /etc/letsencrypt/archive/example:/etc/ssl/certs/example:ro
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/certs/:/etc/nginx/certs:ro
depends_on:
- frontend
- backend
networks:
- app-network
networks:
app-network:
driver: bridge
What I’ve tried:
Verified the backend works locally on https://localhost:5001
.
Confirmed certificates are mounted properly in Docker.
What could be the issue? Is my NGINX configuration wrong, or do I need to adjust Docker networking? How can I fix the 504 Gateway Timeout?
I'm hosting a project on a VPS using Docker. The project consists of:
A React frontend (Vite),
A .NET backend,
NGINX as a reverse proxy.
with a file structure:
project/
├── docker-compose.yml
├── example.client/
│ ├── dockerfile
│ └── build/
├── example.server/
│ ├── dockerfile
│ └── app/
└── nginx/
├── default.conf
├── nginx.conf
└── certs
├── api.example.com
│ ├── privkey.pem
│ └── fullchain.pem
└── example.com
├── privkey.pem
└── fullchain.pem
I want the frontend to work on https://example.com
and the backend on https://api.example.com
. Both should be served over HTTPS using certificates from Let's Encrypt.
I've managed to:
https://example.com
.What I've succesfully achived is frontend to work when going to example.com and to present the contents of frontend, but then when a request is made, I see in a header address https://example.com/api
instead of https://api.example.com/api
so the outcome is 504 after a timeout and it's driving me crazy. I believe there is something wrong with my nginx config, so here are all the configurations I've been using:
NGINX configuration:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/certs/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/example.com/privkey.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://frontend:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api/ {
proxy_pass https://api.example.com/api/;
proxy_set_header Host api.example.com;
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;
proxy_connect_timeout 90;
proxy_send_timeout 90;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
server {
listen 80;
server_name api.example.com www.api.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443;
server_name api.example.com;
ssl_certificate /etc/nginx/certs/api.example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/api.example.com/privkey.pem;
location / {
proxy_pass https://api.example.com:5001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
error_log /var/log/nginx/api_error.log;
access_log /var/log/nginx/api_access.log;
}
docker-compose.yml:
version: '3.9'
services:
backend:
image: example.server
container_name: example.server
build:
context: ./example.server
dockerfile: dockerfile
ports:
- "5000:5000"
- "5001:5001"
environment:
- ASPNETCORE_URLS=https://+:5001
- ASPNETCORE_Kestrel__Certificates__Default__Path=/certs/fullchain.pem
- ASPNETCORE_Kestrel__Certificates__Default__KeyPath=/certs/privkey.pem
volumes:
- /etc/letsencrypt/archive/api.example.com:/certs:ro
networks:
- app-network
frontend:
image: example.client
container_name: example.client
build:
context: ./example.client
dockerfile: dockerfile
ports:
- "3000:80"
depends_on:
- backend
networks:
- app-network
volumes:
- /etc/letsencrypt/archive/api.example.com:/etc/ssl/certs/api.example.com:ro
- /etc/letsencrypt/archive/example.com:/etc/ssl/certs/example.com:ro
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/certs/:/etc/nginx/certs:ro
depends_on:
- frontend
- backend
networks:
- app-network
networks:
app-network:
driver: bridge
What I’ve tried:
Verified the backend works locally on https://localhost:5001
.
Confirmed certificates are mounted properly in Docker.
What could be the issue? Is my NGINX configuration wrong, or do I need to adjust Docker networking? How can I fix the 504 Gateway Timeout?
Normally, the SSL-termination is handled by your nginx-reverse-proxy. So the backend shouldn't know the certificates.
Keep that in mind and your nginx-config should be look like this:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/certs/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/example.com/privkey.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://frontend:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api/ {
proxy_pass http://backend:5000/api/;
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;
proxy_connect_timeout 90;
proxy_send_timeout 90;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}
In your fronted you can now easily access the backend via /api/
.
example.com/api
andapi.example.com/api
? Using double SSL encryption (client toapi.example.com
and thenapi.example.com
tobackend:5001
makes a little sense (just a waste of resources). I'm not sure, but if it is a Docker networking issue, you can try to replace bothproxy_pass https://api.example.com/api/;
andproxy_pass https://api.example.com:5001;
lines withproxy_pass https://backend:5001;
– Ivan Shatsky Commented Jan 7 at 3:01