The django.core.signing.BadSignature error means a signed value has been tampered with or is corrupted.
This typically happens when Django needs to verify a signature it previously created, usually for session cookies, CSRF tokens, or cached data. The signature acts as a checksum, ensuring the data hasn’t changed since it was signed with your secret key. If the signature doesn’t match the data, Django throws this error to prevent potential security issues or data corruption.
Here are the most common reasons for BadSignature errors and how to fix them:
1. Secret Key Mismatch
The most frequent culprit is a change in your SECRET_KEY setting. Django uses this key to generate and verify signatures. If the SECRET_KEY used to sign a value is different from the SECRET_KEY used to verify it, the signature will inherently be invalid.
Diagnosis:
Check your settings.py for the SECRET_KEY. If you’ve recently changed it, this is likely the issue. You can also try to manually sign and unsign a test string using your current SECRET_KEY to see if it works as expected.
from django.core.signing import sign, unsign
SECRET_KEY = 'your-current-secret-key' # Make sure this is the key Django is running with
test_value = 'hello'
signed_value = sign(test_value)
print(f"Signed: {signed_value}")
try:
unsigned_value = unsign(signed_value)
print(f"Unsigned: {unsigned_value}")
except Exception as e:
print(f"Unsigning failed: {e}")
Fix:
If you’ve changed your SECRET_KEY, you must either revert to the old key or invalidate all existing signed data. Reverting is usually the quickest fix if the change was accidental. If the change was intentional (e.g., for security), you’ll need to clear the relevant data. For session cookies, this means users will be logged out and need to log in again. For CSRF tokens, they will be regenerated.
Why it works: Using the same SECRET_KEY for signing and unsigning ensures that the cryptographic hash generated during signing will match the hash generated during verification, assuming the data itself hasn’t changed.
2. Corrupted Signed Data
Sometimes, the signed data itself can become corrupted in transit or storage. This can happen with session cookies if the browser or server-side storage is faulty, or with cached data that gets mangled.
Diagnosis: Examine the specific signed value that’s causing the error. If it’s a cookie, inspect it in your browser’s developer tools. Does it look like a random string, or does it appear truncated or contain unusual characters? If it’s cached data, check the storage mechanism (e.g., Memcached, Redis) for the specific key.
Fix: The most straightforward fix is to clear the corrupted data.
- Session Cookies: If the error occurs with session cookies, clearing the user’s browser cookies can resolve it. On the server-side, if you’re using a database-backed session, you might need to delete the specific session record.
- CSRF Tokens: These are usually regenerated automatically on the next request. If not, ensure your CSRF middleware is correctly configured.
- Cached Data: Clear the cache for the affected item or the entire cache if you can’t pinpoint the specific corrupted entry. For example, with
django.core.cache.backends.memcached.MemcachedCache:
Or to clear all:from django.core.cache import cache cache.delete('your_cache_key')cache.clear()
Why it works: By removing the corrupted signed data, Django will generate new, valid signed data on the next request or cache operation, bypassing the signature check on the old, invalid data.
3. Time-Based Signing Issues (Expired Signatures)
Django’s signing mechanism supports time-based signatures, which expire after a certain duration. If a signature is older than its allowed lifespan, unsign will raise BadSignature.
Diagnosis:
When signing data, you might have specified an expires argument.
from django.core.signing import sign, TimestampSigner
signer = TimestampSigner()
signed_data = signer.sign('some_data', expires=datetime.timedelta(minutes=5))
If you’re trying to unsign data that was signed with TimestampSigner and it’s now expired, you’ll get BadSignature.
Fix: You have two options:
- Increase the expiration time: When signing, use a longer
expiresduration.signer = TimestampSigner() # Extend expiration to 1 hour signed_data = signer.sign('some_data', expires=datetime.timedelta(hours=1)) - Accept expired signatures (with caution): If you encounter this error and it’s acceptable for old data to be processed, you can modify the
unsigncall to ignore expired signatures. This is generally not recommended for security-sensitive data.
A more controlled way is to checkfrom django.core.signing import TimestampSigner, SignatureExpired signer = TimestampSigner() try: unsigned_data = signer.unsign(signed_value, max_age=None) # No max age check except SignatureExpired: # Handle expired signature if necessary, or just ignore pass except Exception as e: # Handle other BadSignature errors passmax_ageexplicitly duringunsign:from django.core.signing import TimestampSigner, SignatureExpired signer = TimestampSigner() try: # Set max_age to a value greater than the original expiry, or None to disable unsigned_data = signer.unsign(signed_value, max_age=60*60) # Allow up to 1 hour old except SignatureExpired: print("Signature is expired.") except Exception as e: print(f"Unsigning failed: {e}")
Why it works: TimestampSigner embeds a timestamp in the signed value. unsign checks this timestamp against the current time and the max_age (or expires during signing) parameter. By adjusting max_age or the original expires value, you control how long a signature remains valid.
4. Incorrect Signing/Unsigning Function Usage
Using the wrong signing function or passing incorrect arguments can lead to BadSignature. For instance, mixing sign and TimestampSigner.unsign, or vice-versa.
Diagnosis:
Review the code where the signed value is generated and where it’s being verified. Ensure you are using the same signing mechanism. If you used sign(), use unsign(). If you used TimestampSigner(), use TimestampSigner.unsign().
Fix: Standardize your signing calls.
- If you used
sign('data'), ensure you useunsign(signed_data). - If you used
TimestampSigner().sign('data', expires=...), ensure you useTimestampSigner().unsign(signed_data).
from django.core.signing import sign, unsign, TimestampSigner
# Incorrect mixing:
signed_by_sign = sign('my data')
# This will likely fail with BadSignature if signed_by_sign has a timestamp or vice-versa
# unsigned_by_ts = TimestampSigner().unsign(signed_by_sign)
# Correct usage:
signed_by_sign = sign('my data')
unsigned_by_unsign = unsign(signed_by_sign)
signer = TimestampSigner()
signed_by_ts = signer.sign('my data', expires=60)
unsigned_by_ts = signer.unsign(signed_by_ts)
Why it works: Each signing function produces a specific format for the signed string. The corresponding unsigning function is designed to parse and verify that specific format. Using mismatched functions means the unsigner won’t understand the signature’s structure or its cryptographic integrity.
5. Signed Value Truncation or Modification by Middleware/Proxies
Sometimes, intermediate layers like web servers (Nginx, Apache) or load balancers might modify or truncate cookie headers, leading to corrupted signed data. This is less common but can happen with very long cookie values.
Diagnosis:
Check your web server configuration (e.g., Nginx client_max_body_size, large_client_header_buffers) and any proxy settings. Inspect the raw HTTP headers to see if the cookie value appears complete.
Fix:
Ensure your web server and proxy configurations are set up to handle large HTTP headers and cookie values. For Nginx, you might need to adjust client_header_buffer_size and large_client_header_buffers.
# Example Nginx configuration snippet
http {
client_header_buffer_size 10k;
large_client_header_buffers 4 10k;
# ... other settings
}
You might also consider reducing the size of the data you are signing if possible, or using a different mechanism for storing large amounts of data.
Why it works: Properly configured web servers and proxies will transmit the full cookie header without modification, ensuring that Django receives the complete, untampered signed value for verification.
6. Case Sensitivity or Encoding Issues
While less common with Django’s default signing, inconsistencies in character encoding or case sensitivity in how the signed string is handled across different systems or storage could theoretically lead to a BadSignature.
Diagnosis: Ensure that the environment where signing occurs and the environment where unsigning occurs are using consistent character encoding (UTF-8 is standard). Also, verify that no part of the process is inadvertently changing the case of the signed string.
Fix:
Explicitly set encoding when reading/writing signed data if you suspect this is an issue, though Django’s signing module typically handles this internally. Ensure your database and file systems are configured for consistent character encoding.
Why it works: Consistent encoding ensures that the byte representation of the signed string is identical when it’s signed and when it’s verified. Case consistency prevents minor variations from invalidating the signature.
The next error you’ll likely encounter after resolving BadSignature is a KeyError if you’re using a cache backend that requires a specific key that might have been deleted or is otherwise unavailable.