Hey,
I didn't find a solution for my problem yet.
I use Traefik as a reverse proxy to route to multiple sites. One is an nginx server hosting a static Gatsby site. And I don't understand how to set it up properly. I actually don't care whether there is a trailing slash or not, I just want it to run without issues So assuming all pages need a trailing slash, I want the redirects to look as follows:
http://example.com/page -> http://example.com/page/
https://example.com/page -> https://example.com/page/
...
Here is my current setting:
Traefik: docker-compose.yml
version: '3.7'
services:
traefik:
image: traefik:v2.2.1
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy
ports:
- 80:80
- 443:443
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/traefik.yml:ro
- ./data/dynamic_conf.yml:/dynamic_conf.yml
- ./data/acme.json:/acme.json
environment:
# Here are my API_KEY and API_SECRET
labels:
- "traefik.enable=true"
# add trailing slash
- "traefik.http.middlewares.add-trailing-slash.chain.middlewares=strip-prefix-1,strip-prefix-2"
- "traefik.http.middlewares.strip-prefix-1.redirectregex.regex=^(https?://[^/]+/[a-z0-9_]+)$$"
- "traefik.http.middlewares.strip-prefix-1.redirectregex.replacement=$${1}/"
- "traefik.http.middlewares.strip-prefix-1.redirectregex.permanent=true"
- "traefik.http.middlewares.strip-prefix-2.stripprefixregex.regex=/[a-z0-9_]+"
# remove trailing slash
- "traefik.http.middlewares.remove-trailing-slash.chain.middlewares=strip-prefix-3,strip-prefix-4"
- "traefik.http.middlewares.strip-prefix-3.redirectregex.regex=^(https?://[^/]+/[a-z0-9_]+)/$$"
- "traefik.http.middlewares.strip-prefix-3.redirectregex.replacement=$${1}"
- "traefik.http.middlewares.strip-prefix-3.redirectregex.permanent=true"
- "traefik.http.middlewares.strip-prefix-4.stripprefixregex.regex=/[a-z0-9_]+"
# global wildcard certificates for our godaddy domains
- "traefik.http.routers.wildcard-certs.tls.certresolver=godaddy"
- "traefik.http.routers.wildcard-certs.tls.domains[0].main=example.com"
- "traefik.http.routers.wildcard-certs.tls.domains[0].sans=*.example.com"
networks:
proxy:
external: true
traefik.yml
api:
dashboard: true
entryPoints:
http:
address: ":80"
#compress: false
http:
redirections:
entryPoint:
to: https
scheme: https
permanent: true
https:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
filename: "/dynamic_conf.yml"
certificatesResolvers:
...
nginx: docker-compose.yml
version: '3.7'
services:
nginx:
image: nginx:latest
restart: always
volumes:
- ./web-data/public:/usr/share/nginx/html/
- ./nginx/gatsby-nginx.conf:/etc/nginx/conf.d/default.conf
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx-secure.entrypoints=https"
- "traefik.http.routers.nginx-secure.rule=Host(`example.com`, `www.example.com`)"
- "traefik.http.routers.nginx-secure.tls=true"
- "traefik.docker.network=proxy"
#- "traefik.http.routers.nginx-secure.middlewares=add-trailing-slash"
networks:
- proxy
- default
networks:
proxy:
external: true
And finally my nginx default.conf looks as follows:
server {
listen 80; #443 ssl http2;
server_name example.com;
root /usr/share/nginx/html;
index index.html;
autoindex off;
charset utf-8;
error_page 404 /404.html;
location ~* \.(?:html)$ {
add_header Cache-Control "no-store";
expires off;
}
location /page-data {
add_header Cache-Control "public, max-age=0, must-revalidate";
}
location = /sw.js {
add_header Cache-Control "public, max-age=0, must-revalidate";
}
location /static {
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~* \.(?:js|css)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
#rewrite ^([^.\?]*[^/])$ $1/ permanent;
#try_files \$uri \$uri/ \$uri/index.html =404
#try_files \$uri \$uri/index.html =404
try_files $uri $uri/ $uri/index.html =404;
}
So this configuration actually works. I can browse all sites, but there is one issue. Traefik redirects https to http if there is no trailing slash. For instance:
ben@happiness:~/public$ curl -I https://example.com/contact
HTTP/1.1 301 Moved Permanently
Content-Length: 169
Content-Type: text/html
Date: Sat, 08 Aug 2020 09:36:25 GMT
Location: http://example.com/contact/
Server: nginx/1.19.1
Which in turn redirects to the final destination:
ben@happiness:~/public$ curl -I http://example.com/contact/
HTTP/1.1 308 Permanent Redirect
Location: https://example.com/contact/
Date: Sat, 08 Aug 2020 09:37:33 GMT
Content-Length: 18
Content-Type: text/plain; charset=utf-8
Not only that it is ugly to redirect https to http and probably insecure, but also my Mautic form doesn't work properly (i.e., doesn't redirect to the thank-you page and doesn't show if a mandatory field has not been filled).
So I played with adding a trailing slash (cf. the two docker-compose.yml). Specifically, if I uncomment in my nginx docker-compose.yml the following line:
- "traefik.http.routers.nginx-secure.middlewares=add-trailing-slash"
With forced trailing slashes redirects look okay:
ben@happiness:~/public$ curl -I https://example.com/contact
HTTP/1.1 308 Permanent Redirect
Location: https://example.com/contact/
Date: Sat, 08 Aug 2020 09:42:20 GMT
Content-Length: 18
Content-Type: text/plain; charset=utf-8
Yet, I don't know how to setup nginx in this case. In particular, nginx runs into several 404s when accessing the site:
VM684:1 GET https://example.com/page-data/app-data.json 404
(anonymous) @ VM684:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
C @ app-fd0fd20afca3d57009ab.js:1
t.memoizedGet @ app-fd0fd20afca3d57009ab.js:1
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
t.loadPage @ app-fd0fd20afca3d57009ab.js:1
loadPage @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
UxWs @ app-fd0fd20afca3d57009ab.js:1
a @ webpack-runtime-703898b324de4f9dc223.js:1
t @ webpack-runtime-703898b324de4f9dc223.js:1
r @ webpack-runtime-703898b324de4f9dc223.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
VM684:1 GET https://example.com/page-data/index/page-data.json 404
(anonymous) @ VM684:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
C @ app-fd0fd20afca3d57009ab.js:1
t.memoizedGet @ app-fd0fd20afca3d57009ab.js:1
t.fetchPageDataJson @ app-fd0fd20afca3d57009ab.js:1
t.loadPageDataJson @ app-fd0fd20afca3d57009ab.js:1
r.loadPageDataJson @ app-fd0fd20afca3d57009ab.js:1
t.loadPage @ app-fd0fd20afca3d57009ab.js:1
loadPage @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
UxWs @ app-fd0fd20afca3d57009ab.js:1
a @ webpack-runtime-703898b324de4f9dc223.js:1
t @ webpack-runtime-703898b324de4f9dc223.js:1
r @ webpack-runtime-703898b324de4f9dc223.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
VM684:1 GET https://example.com/page-data/app-data.json 404
(anonymous) @ VM684:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
C @ app-fd0fd20afca3d57009ab.js:1
t.memoizedGet @ app-fd0fd20afca3d57009ab.js:1
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
t.loadPage @ app-fd0fd20afca3d57009ab.js:1
loadPage @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
UxWs @ app-fd0fd20afca3d57009ab.js:1
a @ webpack-runtime-703898b324de4f9dc223.js:1
t @ webpack-runtime-703898b324de4f9dc223.js:1
r @ webpack-runtime-703898b324de4f9dc223.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
VM684:1 GET https://example.com/page-data/404.html/page-data.json 404
(anonymous) @ VM684:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
C @ app-fd0fd20afca3d57009ab.js:1
t.memoizedGet @ app-fd0fd20afca3d57009ab.js:1
t.fetchPageDataJson @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
t.fetchPageDataJson @ app-fd0fd20afca3d57009ab.js:1
t.loadPageDataJson @ app-fd0fd20afca3d57009ab.js:1
r.loadPageDataJson @ app-fd0fd20afca3d57009ab.js:1
t.loadPage @ app-fd0fd20afca3d57009ab.js:1
loadPage @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
UxWs @ app-fd0fd20afca3d57009ab.js:1
a @ webpack-runtime-703898b324de4f9dc223.js:1
t @ webpack-runtime-703898b324de4f9dc223.js:1
r @ webpack-runtime-703898b324de4f9dc223.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
VM684:1 GET https://example.com/page-data/app-data.json 404
(anonymous) @ VM684:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
C @ app-fd0fd20afca3d57009ab.js:1
t.memoizedGet @ app-fd0fd20afca3d57009ab.js:1
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
t.loadPage @ app-fd0fd20afca3d57009ab.js:1
loadPage @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
UxWs @ app-fd0fd20afca3d57009ab.js:1
a @ webpack-runtime-703898b324de4f9dc223.js:1
t @ webpack-runtime-703898b324de4f9dc223.js:1
r @ webpack-runtime-703898b324de4f9dc223.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
VM684:1 GET https://example.com/page-data/app-data.json 404
(anonymous) @ VM684:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
C @ app-fd0fd20afca3d57009ab.js:1
t.memoizedGet @ app-fd0fd20afca3d57009ab.js:1
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
t.loadAppData @ app-fd0fd20afca3d57009ab.js:1
t.loadPage @ app-fd0fd20afca3d57009ab.js:1
loadPage @ app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
UxWs @ app-fd0fd20afca3d57009ab.js:1
a @ webpack-runtime-703898b324de4f9dc223.js:1
t @ webpack-runtime-703898b324de4f9dc223.js:1
r @ webpack-runtime-703898b324de4f9dc223.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
app-fd0fd20afca3d57009ab.js:1 Uncaught (in promise) Error: page resources for / not found. Not rendering React
at app-fd0fd20afca3d57009ab.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
Promise.then (async)
UxWs @ app-fd0fd20afca3d57009ab.js:1
a @ webpack-runtime-703898b324de4f9dc223.js:1
t @ webpack-runtime-703898b324de4f9dc223.js:1
r @ webpack-runtime-703898b324de4f9dc223.js:1
(anonymous) @ app-fd0fd20afca3d57009ab.js:1
manifest.json:1 GET https://example.com/icons-2282555dbdb424e391e15f39684cacbd/manifest.json 404
manifest.json:1 Manifest: Line: 1, column: 1, Syntax error.
I highly appreciate any pointer on how to fix these issues!
It would also be great to learn how to setup http2 in this scenario!
Thanks!
Ben