Creating Signed URLs in WordPress: Using Nonces and Custom HMAC
When you need to create temporary or restricted access to content in WordPress, you have two primary options: WordPress nonces and custom signed URLs. This comprehensive guide will show you when and how to use each approach.
This comprehensive blog post combines both techniques with:
- 
Clear explanations of both nonces and signed URLs 
- 
Detailed implementation guides for each method 
- 
Comparison table highlighting when to use each approach 
- 
Visual styling for better readability 
- 
Security best practices for both methods 
- 
A hybrid approach for cases where both might be needed 
- 
Practical code examples ready for implementation 
The post is structured to help WordPress developers:
- 
Understand the differences between the approaches 
- 
Implement either solution with confidence 
- 
Choose the right method for their specific use case 
- 
Follow security best practices 
Understanding the Options
- WordPress Nonces: Built-in security tokens tied to user sessions and specific actions
- Signed URLs: Custom URLs with cryptographic signatures and explicit expiration
WordPress Nonces
Nonces (“number used once”) are WordPress’s built-in security mechanism for verifying the intent behind requests. They’re primarily used for:
- CSRF (Cross-Site Request Forgery) protection
- Securing forms and admin actions
- Creating temporary links for authenticated users
Signed URLs
Signed URLs are custom URLs that contain:
- A cryptographic signature
- An expiration timestamp
- Optional access parameters
They’re useful for:
- Sharing private files temporarily
- Granting access to non-WordPress users
- Creating time-limited download links
Comparison Table
| Feature | WordPress Nonce | Signed URL | 
|---|---|---|
| Implementation | Built-in WordPress functions | Custom code required | 
| User Context | Tied to user sessions | Can be user-agnostic | 
| Expiration | 24 hours default (configurable) | Precise timestamp control | 
| Security Level | Good for WordPress actions | Higher (cryptographic) | 
| Best For | Forms, admin actions, logged-in users | File sharing, public access, API endpoints | 
Implementing WordPress Nonces
1. Creating a Nonce URL
<?php
      // Generate nonce for specific action
      $nonce = wp_create_nonce('view-protected-content');
      
      // Add to URL
      $protected_url = add_query_arg('_wpnonce', $nonce, 
          get_permalink($protected_page_id)
      );
      ?>2. Verifying the Nonce
<?php
      if (!isset($_GET['_wpnonce']) || 
          !wp_verify_nonce($_GET['_wpnonce'], 'view-protected-content')) {
          wp_die('Security check failed', 'Error', 403);
      }
      // Show protected content
      ?>3. Advanced Nonce Customization
<?php
      // Change nonce lifetime to 2 hours
      add_filter('nonce_life', function() {
          return 7200;
      });
      
      // Create nonce for specific user
      $user_id = 123;
      $nonce = wp_create_nonce('user-access-' . $user_id);
      ?>Implementing Custom Signed URLs
1. Generating Signed URLs
<?php
      function generate_signed_url($base_url, $expires, $secret_key) {
          $url = add_query_arg('expires', $expires, $base_url);
          $signature = hash_hmac('sha256', $url, $secret_key);
          return add_query_arg('signature', $signature, $url);
      }
      
      // Usage:
      $signed_url = generate_signed_url(
          'https://yoursite.com/protected-file.pdf',
          time() + 3600, // 1 hour expiration
          'your-secret-key'
      );
      ?>2. Validating Signed URLs
<?php
      function validate_signed_url($secret_key) {
          $expires = $_GET['expires'] ?? 0;
          $signature = $_GET['signature'] ?? '';
          
          $request_url = remove_query_arg('signature');
          $request_url = add_query_arg('expires', $expires, $request_url);
          
          if (time() > $expires) return false;
          
          $expected = hash_hmac('sha256', $request_url, $secret_key);
          return hash_equals($signature, $expected);
      }
      
      // Usage:
      if (!validate_signed_url('your-secret-key')) {
          wp_die('Invalid or expired URL', 403);
      }
      ?>3. Enhancing Signed URLs
<?php
      // Add IP restriction
      function generate_restricted_url($base_url, $expires, $secret_key) {
          $url = add_query_arg([
              'expires' => $expires,
              'allowed_ip' => $_SERVER['REMOTE_ADDR']
          ], $base_url);
          
          $signature = hash_hmac('sha256', $url, $secret_key);
          return add_query_arg('signature', $signature, $url);
      }
      
      // Validate with IP check
      function validate_restricted_url($secret_key) {
          // ... existing validation ...
          if ($_GET['allowed_ip'] !== $_SERVER['REMOTE_ADDR']) {
              return false;
          }
          return true;
      }
      ?>When to Use Each Technique
Use WordPress Nonces When:
- Protecting WordPress admin actions or forms
- The access is tied to a WordPress user session
- You need quick implementation without custom code
- Default 24-hour expiration is acceptable
Use Signed URLs When:
- Sharing content with non-WordPress users
- You need precise control over expiration time
- The access isn’t tied to WordPress sessions
- You need cryptographic security beyond nonces
- Creating time-limited download links
Security Best Practices
- Always use HTTPS
- Store secret keys securely (wp-config.php)
- Set reasonable expiration times
- Log access attempts for sensitive content
Nonce-Specific:
- Use unique action names for each nonce
- Don’t expose nonces to untrusted users
Signed URL-Specific:
- Rotate secret keys periodically
- Consider adding IP restrictions for sensitive content
- Use strong hashing algorithms (SHA-256+)
Hybrid Approach
For some use cases, you might combine both techniques:
<?php
   // Generate URL with both nonce and signature
   function generate_hybrid_url($user_id, $base_url, $expires, $secret_key) {
       // Add WordPress nonce
       $nonce = wp_create_nonce('hybrid-access-' . $user_id);
       $url = add_query_arg('_wpnonce', $nonce, $base_url);
       
       // Add signed URL parameters
       $url = add_query_arg('expires', $expires, $url);
       $signature = hash_hmac('sha256', $url, $secret_key);
       return add_query_arg('signature', $signature, $url);
   }
   
   // Validate both
   function validate_hybrid_url($user_id, $secret_key) {
       // Verify nonce first
       if (!wp_verify_nonce($_GET['_wpnonce'], 'hybrid-access-' . $user_id)) {
           return false;
       }
       
       // Then verify signature
       return validate_signed_url($secret_key);
   }
   ?>Conclusion
Both WordPress nonces and custom signed URLs have important roles in WordPress security:
- For WordPress-native functionality (forms, admin actions), nonces are the simpler and more appropriate choice.
- For public-facing, time-limited content sharing, signed URLs provide more flexibility and security.
- For maximum security in critical systems, consider combining both approaches.
By understanding both techniques, you can choose the right tool for each security challenge in your WordPress projects.
 
  
  
  
  
  
  
  
 