TL;DR
This guide shows how to safely use Background Sync in your web app, protecting against Cross-Site Request Forgery (CSRF) attacks. We’ll focus on using a unique token for each sync request and verifying it on the server.
Background Sync & CSRF: The Problem
Background Sync lets users continue working offline; changes are queued and sent when they regain connectivity. However, this introduces a CSRF risk. A malicious site could trigger a sync operation without the user’s knowledge, potentially performing unwanted actions on your server.
Solution: Unique Tokens & Verification
The core idea is to include a unique, unpredictable token in each Background Sync request and verify that token on the server before processing the data. Here’s how:
Step-by-Step Guide
- Generate a CSRF Token: On your server, generate a new, random CSRF token for each user session. Don’t reuse tokens! Store this token securely associated with the user’s session.
- Include the Token in Your Form/Request: When creating data that you want to sync, include the current CSRF token as a hidden field or header within your request payload. For example:
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"> - Pass Token to Service Worker: When the user submits a form or triggers an action that needs syncing, retrieve the CSRF token from your application and store it in local storage. This is needed by the service worker.
localStorage.setItem('csrf_token', '{{ csrf_token }}'); - Service Worker Registration & Sync Event: Register your service worker as usual. The sync event will be triggered when connectivity returns.
if ('serviceWorker' in navigator) {n navigator.serviceWorker.register('/sw.js').then(function(registration) {n console.log('Service Worker registered!');n });n} - Retrieve Token in Service Worker: Inside your service worker, retrieve the CSRF token from local storage when handling the sync event.
self.addEventListener('sync', function(event) {n const csrfToken = localStorage.getItem('csrf_token');n // ... - Construct Sync Request with Token: Build your POST request to send the queued data, including the retrieved CSRF token in a header (recommended) or as part of the payload.
fetch('/sync-endpoint', {n method: 'POST',n headers: {n 'Content-Type': 'application/json',n 'X-CSRF-Token': csrfToken // Use a custom headern },n body: JSON.stringify(queuedData)n}).then(...); - Server-Side Verification: On your server, always verify the CSRF token received with the sync request against the token stored in the user’s session.
- If the tokens match, process the data.
- If the tokens don’t match, reject the request immediately (return a 403 Forbidden error).
- Token Rotation: Implement token rotation on your server for added security. Regularly generate new CSRF tokens and invalidate old ones. This limits the window of opportunity for attackers if a token is compromised.
Important Considerations
- Secure Storage: Ensure that local storage is used securely. While generally safe, avoid storing sensitive information directly in local storage.
- HTTPS Only: Background Sync requires HTTPS to function correctly and prevent man-in-the-middle attacks.
- Error Handling: Implement robust error handling in your service worker to gracefully handle failed sync attempts.
- Session Management: Proper session management on the server is crucial for CSRF protection. Ensure sessions are invalidated when users log out or after a period of inactivity.

