|
| 1 | +# Ghost Cache Invalidation Proxy |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This proxy sits between a Ghost CMS instance and clients. It monitors responses from Ghost for the `X-Cache-Invalidate` header and triggers configured webhooks when cache invalidation is needed. |
| 6 | + |
| 7 | +When Ghost updates content, it includes an `X-Cache-Invalidate` header in its responses to indicate which content needs cache invalidation. This proxy captures that header and forwards the information to a configurable webhook endpoint, allowing integration with any cache service or CDN. |
| 8 | + |
| 9 | +## Project History |
| 10 | + |
| 11 | +This project evolved from [ghost-bunnycdn-perma-cache-purger](https://github.com/magicpages/ghost-bunnycdn-perma-cache-purger), which was specifically designed to work with BunnyCDN. While the original project served its purpose well, this version has been abstracted to work with any webhook-capable CDN or cache system, making it more versatile for different hosting setups. The core functionality of monitoring Ghost's X-Cache-Invalidate headers remains the same, but the cache purging mechanism has been generalized to support configurable webhooks. |
| 12 | + |
| 13 | +## Usage |
| 14 | + |
| 15 | +The `magicpages/ghost-cache-invalidation-proxy` Docker image is available on [Docker Hub](https://hub.docker.com/r/magicpages/ghost-cache-invalidation-proxy). It can be used to deploy the proxy as part of a Docker Compose stack alongside Ghost. |
| 16 | + |
| 17 | +### Environment Variables |
| 18 | + |
| 19 | +#### Required variables |
| 20 | + |
| 21 | +- `GHOST_URL`: The URL of your Ghost CMS instance. Ideally, the hostname of your Ghost container and the port it listens on (e.g., `http://ghost:2368`). |
| 22 | +- `WEBHOOK_URL`: The URL of the webhook endpoint to call when cache invalidation is needed. |
| 23 | + |
| 24 | +#### Optional variables |
| 25 | + |
| 26 | +- `PORT`: The port on which the proxy listens for incoming requests. Defaults to `3000`. |
| 27 | +- `DEBUG`: Set to `true` to enable debug logging. Defaults to `false`. |
| 28 | +- `WEBHOOK_METHOD`: HTTP method to use for the webhook call. Defaults to `POST`. |
| 29 | +- `WEBHOOK_SECRET`: Secret key for webhook authentication. Will be used in the Authorization header if provided. |
| 30 | +- `WEBHOOK_HEADERS`: JSON string of additional headers to include in the webhook request. |
| 31 | +- `WEBHOOK_BODY_TEMPLATE`: JSON template for the webhook request body. Supports the following variables: |
| 32 | + - `${urls}`: Array of URLs/patterns from the `X-Cache-Invalidate` header. |
| 33 | + - `${purgeAll}`: Boolean indicating if all cache should be purged. |
| 34 | + - `${timestamp}`: Current timestamp. |
| 35 | + - `${pattern}`: Raw pattern from the `X-Cache-Invalidate` header. |
| 36 | +- `WEBHOOK_RETRY_COUNT`: Number of retry attempts for failed webhook calls. Defaults to `3`. |
| 37 | +- `WEBHOOK_RETRY_DELAY`: Delay in milliseconds between retry attempts. Defaults to `1000`. |
| 38 | + |
| 39 | +### Example Docker Compose Configuration |
| 40 | + |
| 41 | +```yaml |
| 42 | +version: '3.8' |
| 43 | + |
| 44 | +services: |
| 45 | + ghost: |
| 46 | + image: ghost:5 |
| 47 | + environment: |
| 48 | + url: http://localhost:4000 |
| 49 | + database__client: sqlite3 |
| 50 | + database__connection__filename: /var/lib/ghost/content/data/ghost.db |
| 51 | + volumes: |
| 52 | + - ghost_data:/var/lib/ghost/content |
| 53 | + |
| 54 | + cache-invalidation: |
| 55 | + image: magicpages/ghost-cache-invalidation-proxy:latest |
| 56 | + environment: |
| 57 | + - GHOST_URL=http://ghost:2368 |
| 58 | + - PORT=4000 |
| 59 | + - DEBUG=true |
| 60 | + - WEBHOOK_URL=https://api.example.com/invalidate |
| 61 | + - WEBHOOK_METHOD=POST |
| 62 | + - WEBHOOK_SECRET=your_secret_key |
| 63 | + - WEBHOOK_HEADERS={"Custom-Header": "Value"} |
| 64 | + - WEBHOOK_BODY_TEMPLATE={"urls": ${urls}, "timestamp": "${timestamp}", "purgeAll": ${purgeAll}} |
| 65 | + ports: |
| 66 | + - "4000:4000" |
| 67 | + depends_on: |
| 68 | + - ghost |
| 69 | + |
| 70 | +volumes: |
| 71 | + ghost_data: |
| 72 | +``` |
| 73 | +
|
| 74 | +## Integration Examples |
| 75 | +
|
| 76 | +### BunnyCDN Integration |
| 77 | +
|
| 78 | +To use this with BunnyCDN, set up your webhook configuration like this: |
| 79 | +
|
| 80 | +``` |
| 81 | +WEBHOOK_URL=https://api.bunny.net/purge |
| 82 | +WEBHOOK_METHOD=POST |
| 83 | +WEBHOOK_SECRET=your_bunnycdn_api_key |
| 84 | +WEBHOOK_HEADERS={"AccessKey": "${secret}", "Content-Type": "application/json"} |
| 85 | +WEBHOOK_BODY_TEMPLATE={"urls": ${urls}} |
| 86 | +``` |
| 87 | +
|
| 88 | +### Cloudflare Integration |
| 89 | +
|
| 90 | +For Cloudflare: |
| 91 | +
|
| 92 | +``` |
| 93 | +WEBHOOK_URL=https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/purge_cache |
| 94 | +WEBHOOK_METHOD=POST |
| 95 | +WEBHOOK_SECRET=your_cloudflare_api_token |
| 96 | +WEBHOOK_HEADERS={"Authorization": "Bearer ${secret}", "Content-Type": "application/json"} |
| 97 | +WEBHOOK_BODY_TEMPLATE={"files": ${urls}} |
| 98 | +``` |
| 99 | +
|
| 100 | +## How It Works |
| 101 | +
|
| 102 | +1. The proxy forwards all client requests to the Ghost CMS instance. |
| 103 | +2. When Ghost responds, the proxy checks for the `X-Cache-Invalidate` header. |
| 104 | +3. If the header is present, the proxy extracts the invalidation patterns and constructs a webhook payload. |
| 105 | +4. The webhook is called with the configured parameters, headers, and body. |
| 106 | +5. The proxy supports retries for failed webhook calls. |
| 107 | + |
| 108 | +## License |
| 109 | + |
| 110 | +This project is licensed under the MIT License. |
| 111 | + |
| 112 | +## Contributing |
| 113 | + |
| 114 | +If you have any ideas for improvements or new features, feel free to open an issue or submit a pull request. |
0 commit comments