1. Caddy version (caddy version
):
v2.4.2 h1:chB106RlsIaY4mVEyq9OQM5g/9lHYVputo/LAX2ndFg=
2. How I run Caddy:
I’m running it through Docker using the caddy:2-alpine Docker image. Here’s the Caddyfile I’m using:
The Docker container is behind a load balancer that handles SSL termination, hence why there’s no hostname and it’s just running on port 80 with no HTTPS.
This is working fine except for requests to the root, where it redirects infinitely with a 308 Permanent Redirect. Changing it to /index.html manually works as expected, as do other URLs in our PWA.
a. System environment:
Running in Docker on Linux. Docker version 20.10.7, build f0df350.
b. Command:
docker run -it -p 8080:80 web:latest
c. Service/unit/compose file:
Dockerfile
FROM node:12 as build
WORKDIR /usr/src/app
# Build arguments
ARG SENTRY_RELEASE_TOKEN
ARG REACT_APP_API_URL
ARG REACT_APP_COGNITO_USER_POOL_ID
ARG REACT_APP_COGNITO_URI
ARG REACT_APP_COGNITO_CLIENT_ID
ARG REACT_APP_COGNITO_REDIRECT_URI
ARG FONTAWESOME_NPM_AUTH_TOKEN
COPY package.json yarn.lock .npmrc ./
RUN yarn
COPY . ./
RUN NODE_OPTIONS=--max_old_space_size=8192 yarn build
FROM caddy:2-alpine
# Bash is needed for the script that generates the config based on runtime env vars
RUN apk update && apk add bash
COPY Caddyfile /etc/caddy/Caddyfile
COPY .config /srv/.config
COPY config.sh /srv/config.sh
COPY entrypoint.sh /srv/entrypoint.sh
COPY --from=build /usr/src/app/build /srv
EXPOSE 80
CMD ["/bin/bash", "-c", "/srv/entrypoint.sh"]
Entrypoint
#!/bin/bash
cd /srv
# Generate the config based on runtime env vars
./config.sh ./config.js
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
d. My complete Caddyfile or JSON config:
{
admin :2019
}
:80
# Set this path to your site's directory.
root * /srv
try_files {path} /index.html
header /images/* Cache-Control max-age=31536000
header /js/* Cache-Control max-age=31536000
header /static/* Cache-Control max-age=31536000
encode gzip
# Enable the static file server.
file_server
3. The problem I’m having:
When running the app fetching the root URL resulted in a 308 Permanent Redirect. Fetching other routes works correctly.
To reproduce, I run the docker image with
docker run -it -p 8080:80 web:latest
Then using CURL:
~ via 💎 v3.0.1 on ☁️ (us-east-1)
❯ curl -v localhost:8080/submissions
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /submissions HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 4503
< Content-Type: text/html; charset=utf-8
< Etag: "qwxcga3h3"
< Last-Modified: Tue, 27 Jul 2021 22:14:34 GMT
< Server: Caddy
< Date: Tue, 27 Jul 2021 22:51:44 GMT
<
<!doctype html><html lang="en"><head><script src="/config.js"></script><script src="//fast.appcues.com/62154.js"></script><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.png"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><title>Relay</title><script src="https://polyfill.io/v3/polyfill.min.js?features=smoothscroll%2Cfetch%2Cconsole.warn%2Cconsole.table%2Cconsole.log%2Cconsole.info%2Cconsole.groupEnd%2Cconsole.group%2Cconsole.exception%2Cconsole.error%2Cconsole%2CResizeObserver%2Ces7%2Ces6%2Ces2019%2Ces2018%2Ces2017%2Ces2016%2Ces2015%2Cdefault%2Cblissfuljs%2Ces5%2Ces2021"></script><script defer="defer" src="https://js.chargebee.com/v2/chargebee.js" data-cb-site="%REACT_APP_CHARGEBEE_SITE%"></script><link href="/static/css/4.c3aead2b.chunk.css" rel="stylesheet"><link href="/static/css/main.6d8b0bdd.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function t(t){for(var n,o,c=t[0],i=t[1],l=t[2],f=0,d=[];f<c.length;f++)o=c[f],Object.prototype.hasOwnProperty.call(a,o)&&a[o]&&d.push(a[o][0]),a[o]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(s&&s(t);d.length;)d.shift()();return u.push.apply(u,l||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,o=1;o<r.length;o++){var i=r[o];0!==a[i]&&(n=!1)}n&&(u.splice(t--,1),e=c(c.s=r[0]))}return e}var n={},o={3:0},a={3:0},u=[];function c(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,c),r.l=!0,r.exports}c.e=function(e){var t=[];o[e]?t.push(o[e]):0!==o[e]&&{5:1,6:1}[e]&&t.push(o[e]=new Promise((function(t,r){for(var n="static/css/"+({}[e]||e)+"."+{0:"31d6cfe0",1:"31d6cfe0",5:"cacc27b1",6:"f524e9ed",7:"31d6cfe0"}[e]+".chunk.css",a=c.p+n,u=document.getElementsByTagName("link"),i=0;i<u.length;i++){var l=(s=u[i]).getAttribute("data-href")||s.getAttribute("href");if("stylesheet"===s.rel&&(l===n||l===a))return t()}var f=document.getElementsByTagName("style");for(i=0;i<f.length;i++){var s;if((l=(s=f[i]).getAttribute("data-href"))===n||l===a)return t()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=t,d.onerror=function(t){var n=t&&t.target&&t.target.src||a,u=new Error("Loading CSS chunk "+e+" failed.\n("+n+")");u.code="CSS_CHUNK_LOAD_FAILED",u.request=n,delete o[e],d.parentNode.removeChild(d),r(u)},d.href=a,document.getElementsByTagName("head")[0].appendChild(d)})).then((function(){o[e]=0})));var r=a[e];if(0!==r)if(r)t.push(r[2]);else{var n=new Promise((function(t,n){r=a[e]=[t,n]}));t.push(r[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,c.nc&&i.setAttribute("nonce",c.nc),i.src=function(e){return c.p+"static/js/"+({}[e]||e)+"."+{0:"9c29a685",1:"2a802b0a",5:"beb3deb5",6:"fb725f4c",7:"51163e9f"}[e]+".chunk.js"}(e);var l=new Error;u=function(t){i.onerror=i.onload=null,clearTimeout(f);var r=a[e];if(0!==r){if(r){var n=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;l.message="Loading chunk "+e+" failed.\n("+n+": "+o+")",l.name="ChunkLoadError",l.type=n,l.request=o,r[1](l)}a[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(t)},c.m=e,c.c=n,c.d=function(e,t,r){c.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},c.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(e,t){if(1&t&&(e=c(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(c.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)c.d(r,n,function(t){return e[t]}.bind(null,n));return r},c.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(t,"a",t),t},c.o=f* Connection #0 to host localhost left intact
unction(e,t){return Object.prototype.hasOwnProperty.call(e,t)},c.p="/",c.oe=function(e){throw console.error(e),e};var i=this["webpackJsonprelay-web"]=this["webpackJsonprelay-web"]||[],l=i.push.bind(i);i.push=t,i=i.slice();for(var f=0;f<i.length;f++)t(i[f]);var s=l;r()}([])</script><script src="/static/js/4.93dced44.chunk.js"></script><script src="/static/js/main.3325c584.chunk.js"></script></body></html>%
~ via 💎 v3.0.1 on ☁️ (us-east-1)
❯ curl -v localhost:8080/
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Content-Type: text/html; charset=utf-8
< Location: /
< Server: Caddy
< Date: Tue, 27 Jul 2021 22:53:37 GMT
< Content-Length: 37
<
<a href="/">Permanent Redirect</a>.
* Connection #0 to host localhost left intact
The first request (to /submissions
correctly returns the PWA). The second redirects to itself, which is incorrect.
4. Error messages and/or full log output:
No error, just the CURL output above.
5. What I already tried:
I tried adding a specific rewrite for the root URL above the try_files directive, but it didn’t change anything.
rewrite / /index.html
6. Links to relevant resources:
None (yet)