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; }