How to Pass POST Parameters with HTML SSE (EventSource): A Step-by-Step Guide for PHP Scripts

Server-Sent Events (SSE) is a powerful web technology that enables servers to push real-time updates to clients over a single, long-lived HTTP connection. Unlike WebSockets (which are bidirectional), SSE is unidirectional—ideal for scenarios like live dashboards, notifications, or real-time logs.

By default, SSE uses the EventSource API in browsers, which only supports HTTP GET requests. This limitation can be frustrating if you need to send sensitive data (e.g., authentication tokens), large payloads, or parameters that require a POST request (e.g., due to server-side constraints).

In this guide, we’ll walk through a practical workaround to pass POST parameters with SSE using PHP. We’ll use a combination of AJAX (to send POST data) and server-side session storage (to retain the data for the SSE connection). By the end, you’ll be able to securely send POST parameters and receive real-time updates via SSE.

Table of Contents#

  1. Understanding SSE and EventSource Limitations
  2. Why Use POST Parameters with SSE?
  3. The Workaround: AJAX + Session Storage
  4. Step-by-Step Implementation
  5. Testing the Workflow
  6. Troubleshooting Common Issues
  7. Security Considerations
  8. Conclusion
  9. References

1. Understanding SSE and EventSource Limitations#

Before diving into the solution, let’s clarify how SSE and EventSource work:

What is SSE?#

SSE establishes a persistent connection between client and server. The server sends data in a special text/event-stream format, and the client listens for message events to process updates.

The EventSource Limitation#

The EventSource API in browsers is designed for simplicity, but it has strict constraints:

  • Only supports GET requests.
  • No built-in support for custom headers or POST parameters.
  • Limited to UTF-8 text payloads (though you can serialize JSON).

This means you can’t directly pass POST parameters via EventSource—but we can work around this!

2. Why Use POST Parameters with SSE?#

You might need POST parameters with SSE for scenarios like:

  • Sensitive Data: Avoid exposing tokens, user IDs, or credentials in URLs (GET parameters are visible in logs and browser history).
  • Large Payloads: GET requests have length limits (typically ~2KB-8KB, depending on the browser/server), while POST supports larger data.
  • Server-Side Requirements: Some APIs or server endpoints only accept POST requests (e.g., due to REST conventions or security policies).
  • Complex Data: Sending structured data (e.g., JSON) is more natural with POST than URL-encoded GET parameters.

3. The Workaround: AJAX + Session Storage#

Since EventSource only uses GET, we need a way to “bridge” POST data to the SSE connection. Here’s the high-level approach:

  1. Step 1: Send POST Data via AJAX
    Use JavaScript’s fetch API (or XMLHttpRequest) to send POST parameters to a PHP endpoint (e.g., store_data.php).

  2. Step 2: Store Data in Server-Side Session
    The PHP endpoint will store the POST data in the user’s session (using PHP’s $_SESSION superglobal).

  3. Step 3: Initialize SSE with EventSource
    After the POST data is stored, the client connects to the SSE endpoint (e.g., sse_server.php) via EventSource (a GET request).

  4. Step 4: Retrieve Session Data in SSE Endpoint
    The SSE endpoint reads the stored session data and sends real-time updates back to the client using the POST parameters.

4. Step-by-Step Implementation#

Let’s build this solution with concrete code examples. We’ll need:

  • A client-side HTML/JavaScript file.
  • Two PHP scripts: one to handle POST data (store_data.php) and one to serve SSE updates (sse_server.php).

Prerequisites#

  • A web server with PHP (e.g., Apache, Nginx).
  • Basic knowledge of PHP sessions and JavaScript.

Client-Side Setup (HTML/JavaScript)#

Create an HTML file (index.html) with a button to trigger the POST request and a div to display SSE updates.

<!DOCTYPE html>
<html>
<head>
    <title>SSE with POST Parameters</title>
    <style>
        #messages { margin-top: 20px; padding: 10px; border: 1px solid #ccc; }
        button { padding: 10px 20px; cursor: pointer; }
    </style>
</head>
<body>
    <h1>Real-Time Updates with POST Parameters</h1>
    <button id="sendPostBtn">Send POST Data & Start SSE</button>
    <div id="messages"></div>
 
    <script>
        const sendPostBtn = document.getElementById('sendPostBtn');
        const messagesDiv = document.getElementById('messages');
        let eventSource = null;
 
        // Step 1: Send POST data via AJAX when the button is clicked
        sendPostBtn.addEventListener('click', async () => {
            // Sample POST parameters (customize as needed)
            const postData = {
                user_id: 123,
                auth_token: "secure_token_123",
                filter: "live_updates"
            };
 
            try {
                // Send POST request to store_data.php
                const response = await fetch('store_data.php', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json', // Send data as JSON
                        // Add CSRF token here in production!
                    },
                    body: JSON.stringify(postData)
                });
 
                if (!response.ok) throw new Error('Failed to send POST data');
 
                // Step 3: Initialize SSE after successful POST
                startSSE();
 
            } catch (error) {
                messagesDiv.innerHTML += `<p>Error: ${error.message}</p>`;
            }
        });
 
        // Step 3: Connect to SSE endpoint
        function startSSE() {
            // Close existing connection if active
            if (eventSource) eventSource.close();
 
            // Connect to SSE server (GET request)
            eventSource = new EventSource('sse_server.php');
 
            // Listen for SSE messages
            eventSource.onmessage = (event) => {
                const data = JSON.parse(event.data);
                messagesDiv.innerHTML += `<p>Update: ${data.message} (Using POST param: ${data.user_id})</p>`;
            };
 
            // Handle errors
            eventSource.onerror = (error) => {
                messagesDiv.innerHTML += `<p>SSE Error: ${error.message}</p>`;
                eventSource.close();
            };
 
            // Handle connection open
            eventSource.onopen = () => {
                messagesDiv.innerHTML += `<p>SSE Connection Established!</p>`;
            };
        }
    </script>
</body>
</html>

Server-Side Setup (PHP)#

We’ll create two PHP scripts: one to store POST data in the session and another to serve SSE updates.

Script 1: store_data.php (Store POST Data in Session)#

This script receives the POST data via AJAX, validates it, and stores it in the user’s session.

<?php
// Enable error reporting (disable in production)
error_reporting(E_ALL);
ini_set('display_errors', 1);
 
// Allow CORS (if client and server are on different domains)
header("Access-Control-Allow-Origin: *"); // Restrict to your domain in production
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Allow-Headers: Content-Type");
 
// Only accept POST requests
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405); // Method Not Allowed
    exit('Only POST requests are allowed');
}
 
// Parse JSON POST data
$postData = json_decode(file_get_contents('php://input'), true);
 
// Validate required parameters (customize as needed)
if (empty($postData['user_id']) || empty($postData['auth_token'])) {
    http_response_code(400); // Bad Request
    exit('Missing required parameters: user_id or auth_token');
}
 
// Start PHP session to store data
session_start();
 
// Store POST data in session (overwrite if needed)
$_SESSION['sse_post_data'] = $postData;
 
// Send success response to client
http_response_code(200);
echo json_encode(['status' => 'success', 'message' => 'POST data stored']);

Script 2: sse_server.php (Serve SSE Updates with Stored POST Data)#

This script maintains a long-lived SSE connection, retrieves the stored POST data from the session, and sends real-time updates.

<?php
// Enable error reporting (disable in production)
error_reporting(E_ALL);
ini_set('display_errors', 1);
 
// SSE headers: Keep connection alive and disable caching
header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");
header("Connection: keep-alive");
header("X-Accel-Buffering: no"); // Disable buffering (Nginx specific)
 
// Start session to retrieve stored POST data
session_start();
 
// Check if POST data exists in the session
if (empty($_SESSION['sse_post_data'])) {
    // Send error event and close connection
    echo "event: error\n";
    echo "data: " . json_encode(['message' => 'No POST data found in session']) . "\n\n";
    flush();
    exit();
}
 
// Retrieve POST data from session
$postData = $_SESSION['sse_post_data'];
$userId = $postData['user_id'];
$authToken = $postData['auth_token'];
 
// Optional: Validate auth_token here (e.g., check against database)
if ($authToken !== "secure_token_123") { // Replace with real validation
    echo "event: error\n";
    echo "data: " . json_encode(['message' => 'Invalid auth token']) . "\n\n";
    flush();
    exit();
}
 
// Close the session to avoid locking (critical for SSE!)
session_write_close();
 
// Simulate real-time updates (replace with your logic)
$counter = 0;
while (true) {
    // Generate a sample update (use $postData for dynamic logic)
    $update = [
        'message' => "Real-time update #{$counter} for user {$userId}",
        'user_id' => $userId,
        'timestamp' => date('Y-m-d H:i:s')
    ];
 
    // Send SSE event (format: "data: <json>\n\n")
    echo "data: " . json_encode($update) . "\n\n";
    flush(); // Send data immediately
 
    // Wait 3 seconds before next update (adjust as needed)
    sleep(3);
    $counter++;
 
    // Optional: Add a condition to break the loop (e.g., after 10 updates)
    if ($counter > 10) break;
}
 
// Close connection after updates
echo "event: close\n";
echo "data: " . json_encode(['message' => 'SSE connection closed']) . "\n\n";
flush();

5. Testing the Workflow#

Follow these steps to test the setup:

  1. Host the Files: Place index.html, store_data.php, and sse_server.php in your web server’s root directory (e.g., htdocs for XAMPP).

  2. Start the Server: Launch your web server (e.g., Apache) and PHP.

  3. Open the Client: Navigate to http://localhost/index.html in a browser.

  4. Trigger the Flow: Click the “Send POST Data & Start SSE” button. You should see:

    • A message confirming the SSE connection.
    • Real-time updates every 3 seconds, including the user_id from the POST parameters.

6. Troubleshooting Common Issues#

Issue 1: SSE Connection Hangs or Times Out#

  • Cause: PHP sessions are file-based and lock by default. If session_start() is called in sse_server.php without session_write_close(), the session file remains locked, blocking other requests.
  • Fix: Call session_write_close() immediately after retrieving $_SESSION data in sse_server.php.

Issue 2: No POST Data in Session#

  • Cause: The client didn’t send POST data via AJAX, or the session isn’t shared between store_data.php and sse_server.php.
  • Fix:
    • Ensure session_start() is called in both store_data.php and sse_server.php.
    • Verify the AJAX request in the browser’s DevTools (Network tab) to confirm POST data is sent.

Issue 3: CORS Errors#

  • Cause: The client and server are on different domains, and CORS headers aren’t configured.
  • Fix: Add CORS headers to store_data.php and sse_server.php:
    header("Access-Control-Allow-Origin: https://your-client-domain.com");
    header("Access-Control-Allow-Credentials: true"); // If using cookies

Issue 4: SSE Messages Not Displaying#

  • Cause: Incorrect SSE format (missing \n\n delimiters) or buffering by the web server.
  • Fix:
    • Ensure SSE events end with \n\n (e.g., echo "data: {}\n\n";).
    • Add header("X-Accel-Buffering: no"); to disable Nginx buffering, or use flush() and ob_flush() in Apache.

7. Security Considerations#

  • Session Security: Use HTTPS to encrypt sessions. Set session.cookie_secure = On and session.cookie_httponly = On in php.ini to prevent cookie theft.
  • CSRF Protection: Add a CSRF token to the AJAX POST request (e.g., via a hidden form field or cookie) and validate it in store_data.php.
  • Input Validation: Sanitize and validate all POST parameters in store_data.php (e.g., check user_id is an integer).
  • Auth Token Validation: Replace the hardcoded auth_token check in sse_server.php with real validation (e.g., query a database).

8. Conclusion#

While EventSource doesn’t natively support POST requests, we can work around this by combining AJAX (to send POST data) and server-side sessions (to retain the data for SSE). This approach lets you securely pass sensitive or large parameters while leveraging SSE’s simplicity for real-time updates.

For bidirectional communication (e.g., chat apps), consider WebSockets. But for one-way real-time updates, SSE remains lighter and easier to implement.

9. References#