How to catch 404 and show a maintenance page

1. The problem I’m having:

I’m using caddy to server my react files using file server. I want to show a maintenance page in case the files are not there (status code is 404). When my app is being deployed, the page shows nothing. I want to show a maintenance page here.
Here is my caddy config file:

2. Error messages and/or full log output:


3. Caddy version:

v2.7.5 h1:HoysvZkLcN2xJExEepaFHK92Qgs7xAiCFydN5x5Hs6Q=

4. How I installed and ran Caddy:

a. System environment:

I followed these guides from DO to install caddy:

b. Command:

-

c. Service/unit/compose file:

-

d. My complete Caddy config:

therapidbox.com {
    root * /var/www/web-app/build
    encode gzip
    file_server
    try_files {path} /index.html
}

5. Links to relevant resources:

Use handle_errors:

1 Like

Thank you so much for the help

2 Likes

How can I use handle_errors for backend?
I have an app running at port 3000, and I’m using this caddy config:

api.therapidbox.com {
    reverse_proxy localhost:3000
}

I want to catch all the routes and all HTTP methods. How can I return a JSON response with handle_errors?

I tried:

api.therapidbox.com {
    reverse_proxy localhost:3000
    handle_errors {
        respond `{"errors":[{"message":"Site is down for maintainance."}]}`
    }
}

But only catches GET method and just / route.

If your upstream is responding with status 500 or whatever, then that’s not an “error” in Caddy, it’s just a response to be proxied.

If your upstream is actually down and Caddy fails to connect to it, then that is an error and handle_errors will be used.

If you need to trigger an error in Caddy when the upstream responds with an error status code (to run your handle_errors routes), then use reverse_proxy’s handle_response option reverse_proxy (Caddyfile directive) — Caddy Documentation and then use an error directive within that. Something like this:

reverse_proxy localhost:3000 {
	@error status 500
	handle_response @error {
		error 500
	}
}

handle_errors {
	respond `{"errors":[{"message":"Site is down for maintenance."}]}`
}

Hey Francis,

I changed my caddy config like this:

api.therapidbox.com {
    reverse_proxy localhost:3000 {
        @error status 502
        handle_response @error {
            respond `{"errors":[{"message":"Site is down for maintainance."}]}`
        }
    }
}

But still I don’t get this response when the site is down (when there is nothing running on the port, in this case port 3000). Neither of GET or POST work.

Is there anything that I need to chaneg?

Like I said, is the 502 coming from upstream, or is it when Caddy fails to connect to the upstream? That determines which approach you need to take in your config.

Enable the debug global option, show your logs from when the site is “down”. Use curl -v to make example requests and show the output.

This is the output of curl -v https://api.therapidbox.com/

*   Trying 68.183.97.101:443...
* Connected to api.therapidbox.com (68.183.97.101) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=api.therapidbox.com
*  start date: Nov 10 15:39:28 2023 GMT
*  expire date: Feb  8 15:39:27 2024 GMT
*  subjectAltName: host "api.therapidbox.com" matched cert's "api.therapidbox.com"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* Using Stream ID: 1 (easy handle 0x55c3dd131e90)
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> GET / HTTP/2
> Host: api.therapidbox.com
> user-agent: curl/7.81.0
> accept: */*
> 
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
< HTTP/2 502 
< alt-svc: h3=":443"; ma=2592000
< server: Caddy
< content-length: 0
< date: Mon, 13 Nov 2023 08:51:05 GMT
< 
* Connection #0 to host api.therapidbox.com left intact

And what about your Caddy logs?

Try this again, are you sure you actually reloaded your config after having made that change earlier?

This line was added in caddy logs:

{"level":"error","ts":1699865907.0920258,"logger":"http.log.error","msg":"dial tcp [::1]:5001: connect: connection refused","request":{"remote_ip":"119.30.85.210”, "remote_port":"19063","client_ip":"119.30.85.210","proto":"HTTP/2.0","method":"GET","host":"api.therapidbox.com","uri":"/","headers":{"Sec-Ch-Ua-Platform":["\"macOS\""],"Sec-Fetch-Mode":["navigate"],"Sec-Ch-Ua":["\"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\""],"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36”],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"],"Sec-Fetch-Site":["none"],"Cache-Control":["max-age=0"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Dest":["document"],"Accept-Language":["en-GB,en-US;q=0.9,en;q=0.8"],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-User":["?1"],"Accept-Encoding":["gzip, deflate, br"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"api.therapidbox.com"}},"duration":0.001051218,"status":502,"err_id":"9h7tvyjt7","err_trace":"reverseproxy.statusError (reverseproxy.go:1265)"}

Yes, I did reload after making these changes.

Again, please show your debug logs (not just access logs). Without them, I can’t confirm what you’re saying and we’re working only on assumptions, wasting both our time.

This problem is solved. I had to use handle_errors. Now it is working with postman, but my actual app is facing CORS issue now. Is there a way I can enable CORS in caddy?

You can set headers with the header directive. But CORS issues are typically application-level.

Ok thank you for the help.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.