Why do Apple app store notifications get an HTTP 301 response?

  apache, in-app-purchase, ssl, symfony, webhooks

I’ve been implementing webhook handlers for App store server-to-server notifications so I can get status updates about in-app subscription
purchases. It’s a Symfony 3.4 app running on a Apache 2.4 server. The code that handles the route seems irrelevant, but here’s the beginning and the end, anyway:

 * Webhook functionality for server-to-server notifications
 * about in-app payments from Apple
 * @param Request $request
 * @param AppleSubscriptionManager $subscriptionManager
 * @return JsonResponse
 * @Route("/notify")
public function handleNotification(Request $request, AppleSubscriptionManager $subscriptionManager): JsonResponse
    $statusMessage = ['message' => 'Not implemented.'];
    try {
        $serverEvent = AppleNotification::createFromRequest($request);
    } catch (Exception $e) {
        return new JsonResponse(['error' => 'Invalid request.', $e->getMessage()], 200);
    if ($serverEvent->getPassword() !== $this->appleSecret) {
        // status 200 - no retry please
        return new JsonResponse(['error' => 'Unauthorized.'], 200);

    $retry = false;
    switch ($serverEvent->getNotificationType()) {
        case 'CANCEL':

    if ($retry == true) {
            $httpStatus = 400;
        } else {
            $httpStatus = 200;
        return new JsonResponse($statusMessage, $httpStatus);

You may notice that the route annotation does not specify a method, which means that any method goes. Apple sends the notifications as POST requests.

The access log of Apache shows the following: - - [05/Oct/2020:05:21:49 +0000] "POST /apple/notify HTTP/1.0" 301 686 "-" "Apache-HttpClient/4.5.12 (Java/11.0.5)"            - - [05/Oct/2020:05:21:49 +0000] "GET /apple/notify HTTP/1.0" 200 345 "-" "Apache-HttpClient/4.5.12 (Java/11.0.5)"             - - [05/Oct/2020:05:21:50 +0000] "POST /apple/notify HTTP/1.0" 301 686 "-" "Apache-HttpClient/4.5.12 (Java/11.0.5)"            - - [05/Oct/2020:05:21:50 +0000] "GET /apple/notify HTTP/1.0" 200 277 "-" "Apache-HttpClient/4.5.12 (Java/11.0.5)"             - - [05/Oct/2020:05:37:08 +0000] "POST /apple/notify HTTP/1.0" 301 686 "-" "Apache-HttpClient/4.5.12 (Java/11.0.5)"            - - [05/Oct/2020:05:37:09 +0000] "GET /apple/notify HTTP/1.0" 200 345 "-" "Apache-HttpClient/4.5.12 (Java/11.0.5)"             - - [05/Oct/2020:05:37:10 +0000] "POST /apple/notify HTTP/1.0" 301 686 "-" "Apache-HttpClient/4.5.12 (Java/11.0.5)"            - - [05/Oct/2020:05:37:10 +0000] "GET /apple/notify HTTP/1.0" 200 277 "-" "Apache-HttpClient/4.5.12 (Java/11.0.5)"

I’d like to know where the 301 response (Permanently moved) comes from. I’m not sure if the redirect response is followed because the subsequent request does not always come from the same IP but it does come within one second from the first one.
What seems clear, so far, is that:

  • The endpoint has been correctly configured at the Apple end so that the notification requests do arrive at the correct server.
  • The server does not have a problem with the request (no 4XX error).
  • No unhandled errors occur on the server (no 5XX error).
  • The request is not OK (no 2XX response).
  • There’s no redirect response intended in the code that handles the route "POST /apple/notify"

I’ve been suspecting there’s some problem with the secure connection between Apple and my server, so let’s look into that a bit. The SSL certificate seen on the website belongs to Cloudflare – a CDN/cache through which all the traffic goes. The connection is encrypted (SSL) between Apple and Cloudflare, and also between Cloudflare and Cloudways (my host). I’m not sure if that’s secure enough for Apple but valid certificates are in place with SHA-256 signatures. If we assume that the requirement for the certificate signature algorithm is not met, what would it look like? Where would the log with the error reside? Where would I find the clue if something else went wrong with the handshake?

I’ve been testing the route with Postman but I’m not able to reproduce a 301 response. I’d be interested to see the HTTP headers in the request coming from Apple so I could test it myself.
So I’m looking for an explanation and a way to reproduce the problem. These questions look related:

The answer to those questions might answer this one also.

Source: Symfony Questions