HTTP

428 - Precondition Required

Getting a 428 Precondition Required means the server requires conditional headers (If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since) but your request doesn't include them—the server enforces optimistic concurrency control and won't process requests without version checks. This client-side error (4xx) happens when servers mandate conditional requests to prevent lost updates. Most common when updating resources without ETag validation, but also appears when servers require If-Match for all modifications, delete operations need conditional headers, or security policies enforce version checks.

#Common Causes

  • Frontend: Missing If-Match header for PUT/PATCH requests. No If-None-Match for create operations. Conditional headers not included in delete requests. ETag not retrieved before update.
  • Backend: Server middleware requires conditional headers for all modifications. Security policy enforces optimistic locking. Resource versioning mandates conditional requests. Server configuration requires If-Match for updates.
  • Infrastructure: API gateway enforces conditional header requirements. Load balancer strips conditional headers. Reverse proxy doesn't pass If-Match headers. WAF blocks requests without conditional headers.

Solutions

  1. 1Step 1: Diagnose - Check DevTools Network tab Request Headers—verify if If-Match or If-None-Match headers are present. Review server response for Precondition-Required header. Check if ETag was retrieved first.
  2. 2Step 2: Diagnose - Server logs show which conditional header is missing. Review server configuration for precondition requirements. Check if middleware is enforcing conditional requests. Verify infrastructure header handling.
  3. 3Step 3: Fix - Client-side: Get resource ETag first with GET request. Include If-Match header with ETag value for updates. Add If-None-Match: * for create operations. Retry with conditional headers if 428 occurs.
  4. 4Step 4: Fix - Server-side: Return 428 with Precondition-Required header listing needed headers. Provide clear error messages about required conditions. Configure middleware to require conditional headers.
  5. 5Step 5: Fix - Infrastructure: Ensure load balancer passes conditional headers. Configure reverse proxy to preserve If-Match headers. Update API gateway to allow conditional headers. Review WAF rules.

</>Code Examples

Fetch API: Handle 428 with Conditional Headers
1// Client-side: Handle 428 by adding required conditional headers
2async function updateResource(id, data) {
3  // First, get resource to retrieve ETag
4  const getResponse = await fetch(`/api/resources/${id}`);
5  
6  if (getResponse.status === 428) {
7    // Server requires conditional headers even for GET
8    const preconditionRequired = getResponse.headers.get('Precondition-Required');
9    throw new Error(`Precondition required: ${preconditionRequired}`);
10  }
11  
12  const etag = getResponse.headers.get('ETag');
13  
14  if (!etag) {
15    throw new Error('ETag not found in response');
16  }
17  
18  // Update with If-Match header
19  const updateResponse = await fetch(`/api/resources/${id}`, {
20    method: 'PUT',
21    headers: {
22      'Content-Type': 'application/json',
23      'If-Match': etag, // Required conditional header
24    },
25    body: JSON.stringify(data),
26  });
27  
28  if (updateResponse.status === 428) {
29    // Precondition required - check which header is needed
30    const preconditionRequired = updateResponse.headers.get('Precondition-Required');
31    throw new Error(`Precondition required: ${preconditionRequired}`);
32  }
33  
34  if (updateResponse.status === 412) {
35    // Precondition failed - ETag mismatch
36    throw new Error('Resource was modified by another request');
37  }
38  
39  return updateResponse.json();
40}
41
42// For create operations, use If-None-Match
43async function createResource(data) {
44  const response = await fetch('/api/resources', {
45    method: 'POST',
46    headers: {
47      'Content-Type': 'application/json',
48      'If-None-Match': '*', // Prevents overwriting existing resources
49    },
50    body: JSON.stringify(data),
51  });
52  
53  if (response.status === 428) {
54    throw new Error('Precondition required for create operation');
55  }
56  
57  return response.json();
58}
Express.js: Require Conditional Headers
1// Server-side: Enforce conditional headers
2const express = require('express');
3const app = express();
4
5// Middleware to require conditional headers for modifications
6const requireConditionalHeaders = (req, res, next) => {
7  const method = req.method;
8  
9  // Require conditional headers for PUT, PATCH, DELETE
10  if (['PUT', 'PATCH', 'DELETE'].includes(method)) {
11    const hasIfMatch = req.headers['if-match'];
12    const hasIfNoneMatch = req.headers['if-none-match'];
13    const hasIfModifiedSince = req.headers['if-modified-since'];
14    const hasIfUnmodifiedSince = req.headers['if-unmodified-since'];
15    
16    if (!hasIfMatch && !hasIfNoneMatch && !hasIfModifiedSince && !hasIfUnmodifiedSince) {
17      return res.status(428)
18        .set('Precondition-Required', 'If-Match, If-None-Match')
19        .json({
20          error: 'Precondition Required',
21          message: 'This operation requires conditional headers (If-Match or If-None-Match)',
22          required: ['If-Match', 'If-None-Match'],
23        });
24    }
25  }
26  
27  next();
28};
29
30// Apply to all routes
31app.use('/api/resources', requireConditionalHeaders);
32
33// Update endpoint
34app.put('/api/resources/:id', async (req, res) => {
35  const etag = req.headers['if-match'];
36  const resource = await db.resources.findById(req.params.id);
37  
38  if (!resource) {
39    return res.status(404).json({ error: 'Resource not found' });
40  }
41  
42  // Verify ETag matches
43  if (etag && etag !== resource.etag) {
44    return res.status(412).json({ error: 'Precondition Failed' });
45  }
46  
47  const updated = await db.resources.update(req.params.id, req.body);
48  res.set('ETag', updated.etag).json(updated);
49});
Nginx: Pass Conditional Headers
1# Nginx: Ensure conditional headers reach backend
2server {
3    listen 80;
4    server_name api.example.com;
5    
6    location /api/ {
7        proxy_pass http://backend;
8        proxy_set_header Host $host;
9        proxy_set_header X-Real-IP $remote_addr;
10        
11        # Pass conditional headers to backend
12        proxy_set_header If-Match $http_if_match;
13        proxy_set_header If-None-Match $http_if_none_match;
14        proxy_set_header If-Modified-Since $http_if_modified_since;
15        proxy_set_header If-Unmodified-Since $http_if_unmodified_since;
16        
17        # Don't strip conditional headers
18        proxy_pass_request_headers on;
19    }
20}

Related Errors

Provider Information

This error code is specific to HTTP services. For more information, refer to the official HTTP documentation.

428 - Precondition Required | HTTP Error Reference | Error Code Reference