Failure and maintenance mode for your API
Create a maintenance page for your single-page web application
Useful to create a maintenance page for AngularJS, Angular, Ember.js, ExtJS, Knockout.js, Meteor.js, React, Vue.js, Sveite
History
I recently got the task of implementing a simple maintenance page in our Angular app. The current backend returns a simple HTML maintenance page in the maintenance mode. It sounds easy to catch the HTML page with Angular and display it. Our setup is nothing fancy. We have an Angular frontend application serviced by a REST API. Haproxy, as a reverse proxy and load balancer in front of our backend application servers, makes Haproxy the central point to toggle the maintenance mode.
My first approach for displaying the HTML page was to check the MIME type 'text/html' and then check the content of the HTML content like this:
if ('maintenance' in htmlData) return showMaintPage(htmlData);
Unfortunately, the maintenance page wasn't triggered.
After a while of debugging, I found out that it was because of the CORS issue. I found a solution by adding those CORS headers directly in haproxy here: https://stackoverflow.com/questions/34022378/haproxy-different-503-errorfile-for-options-and-post-methods
The downside was that it always returns the same static CORS headers (defined in the error file options.http) for the OPTIONS requests, even when the default backend is online. If the backend is online, the answer of an OPTIONS request must be answered by our backend because it is a response with dynamic CORS headers.
For security reasons, this is also not a good idea:
Access-Control-Allow-Origin: *
Better define which URL (front-end) is allowed to access the API
Access-Control-Allow-Origin: https://yourapp.yourdomain.com
To generate those headers dynamically, an additional app is required for this.
I'm sure others face the same issue and try to find a simple solution. The idea came up to create a separate service for this, and why not make this public for others?
Failure and Maintenance mode as a separate backend
Example response
HTTP/1.1 503 Service unavailable
Cache-Control: no-cache
Connection: close
Content-Type: application/json
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: X-Requested-With, Authorization, Origin, Content-Type, Version, Cache-Control
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: https://yourdomain.com
Access-Control-Expose-Headers: X-Requested-With, Authorization, Origin, Content-Type, Content-Disposition, Cache-Control
Access-Control-Max-Age: 3600
{
"errorTitle": {
"Maintenance."
},
"errorMessage": {
"We are currently performing maintenance."
}
}
Example haproxy configuration
global
maxconn 5000
log /dev/log local0
stats socket ${HOME}/haproxy/admin.sock user "${USER}" mode 660 level admin
nbthread 4
ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
timeout connect 10s
timeout client 60s
timeout server 60s
timeout check 3s
log global
mode http
option httplog
option forwardfor
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
maxconn 3000
http-send-name-header X-SST-Backend
frontend balancer
bind 127.0.0.1:8080
mode http
default_backend server_backend
backend server_backend
default-server check inter 10s fall 2 rise 2
option httpchk
http-check expect ! rstatus (502|503)
server server1 s1.yourdomain.com:443 cookie server1 ssl verify none
server server1 s2.yourdomain.com:443 cookie server2 ssl verify none
server failure username.maintpage.net:443 check inter 60s fastinter 3s downinter 5s backup ssl verify none
server maintenance username.maintpage.net:443 check inter 60s fastinter 3s downinter 5s disabled ssl verify none
http-request add-header X-Maintpage-Response-Group 9e428b69-47e2-48b7-a694-bb2222bf48c3
nginx configuration example
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:62005/;
proxy_intercept_errors on;
error_page 502 503 = @fallback;
}
location /ws/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://unix:/tmp/websocket.sock;
}
location @fallback {
proxy_set_header X-Maintpage-Response-Group "e8ccf76c-32c6-48d3-b67d-dbd38d474c87";
proxy_pass https://username.maintpage.net;
}
ssl on;
ssl_certificate_key /etc/ssl/wcenter/api.yourdomain.com.key;
ssl_certificate /etc/ssl/wcenter/api.yourdomain.com.pem;
ssl_client_certificate /etc/ssl/wcenter/api.yourdomain.com.ca;
}