Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

nexxai's avatar
Level 37

Deployed service via Bref; getting 419 Page Expired on all forms

Ok so some background first. I am working on an app and wanted to try out bref; I knew that it had a Laravel integration layer so I figured this should be relatively painless.

I installed the packages that their docs recommend, I installed the serverless binary, etc., everything good so far.

I am able to deploy the site AWS without issue. When I browse to the CloudFront distribution, everything shows up correctly. I can run migrations, again without issue.

My problem is when I go to actually register an account, for example. As soon as I submit the form, I get the 419 Page Expired error. Now I know on most normal Laravel apps, that usually means the CSRF token isn't being sent, but I can see in the developer tools that it's part of the POST request, so I don't think that's it. But now I'm stuck as to what else I can look for.

My checklist:

  • It can't be anything database connection or permission related, since the migrations run normally
  • The site key is set in the APP_KEY environment variable that's being pulled down from the Parameter Store (I've currently got it set to a datatype of "String", just in case "SecureString" has some extra decryption step necessary to read and use it)
  • I've confirmed that it is posting back to the correct CloudFront URL (I have not set up any custom domains yet)
  • In the BrefServiceProvider, they automatically set the trustedproxy values to 0.0.0.0/0 and 2000::0/3
  • They also check if the session driver is file, and if so, automatically change the config to use the cookie driver

Any ideas, no matter how wild they might be, are very welcome here.

serverless.yml:

service: my-real-site-name-goes-here

provider:
  name: aws
  # The AWS region in which to deploy (us-east-1 is the default)
  region: us-east-1
  # Environment variables
  environment:
    APP_DEBUG: bref-ssm:/my-real-site-name-goes-here-prod/APP_DEBUG
    APP_ENV: production # Or use ${sls:stage} if you want the environment to match the stage
    APP_KEY: bref-ssm:/my-real-site-name-goes-here-prod/APP_KEY
    APP_NAME: bref-ssm:/my-real-site-name-goes-here-prod/APP_NAME
    APP_URL: bref-ssm:/my-real-site-name-goes-here-prod/APP_URL
    AWS_BUCKET: bref-ssm:/my-real-site-name-goes-here-prod/AWS_BUCKET
    AWS_URL: bref-ssm:/my-real-site-name-goes-here-prod/AWS_URL
    AWS_USE_PATH_STYLE_ENDPOINT: bref-ssm:/my-real-site-name-goes-here-prod/AWS_USE_PATH_STYLE_ENDPOINT
    BROADCAST_DRIVER: bref-ssm:/my-real-site-name-goes-here-prod/BROADCAST_DRIVER
    CACHE_DRIVER: bref-ssm:/my-real-site-name-goes-here-prod/CACHE_DRIVER
    CASHIER_CURRENCY: bref-ssm:/my-real-site-name-goes-here-prod/CASHIER_CURRENCY
    DB_CONNECTION: bref-ssm:/my-real-site-name-goes-here-prod/DB_CONNECTION
    DB_DATABASE: bref-ssm:/my-real-site-name-goes-here-prod/DB_DATABASE
    DB_HOST: bref-ssm:/my-real-site-name-goes-here-prod/DB_HOST
    DB_PASSWORD: bref-ssm:/my-real-site-name-goes-here-prod/DB_PASSWORD
    DB_PORT: bref-ssm:/my-real-site-name-goes-here-prod/DB_PORT
    DB_USERNAME: bref-ssm:/my-real-site-name-goes-here-prod/DB_USERNAME
    DEMO_MODE_ENABLED: bref-ssm:/my-real-site-name-goes-here-prod/DEMO_MODE_ENABLED
    DYNAMODB_CACHE_TABLE: !Ref CacheTable
    FILESYSTEM_DISK: bref-ssm:/my-real-site-name-goes-here-prod/FILESYSTEM_DISK
    LOG_CHANNEL: bref-ssm:/my-real-site-name-goes-here-prod/LOG_CHANNEL
    LOG_DEPRECATIONS_CHANNEL: bref-ssm:/my-real-site-name-goes-here-prod/LOG_DEPRECATIONS_CHANNEL
    LOG_LEVEL: bref-ssm:/my-real-site-name-goes-here-prod/LOG_LEVEL
    MAIL_FROM_ADDRESS: bref-ssm:/my-real-site-name-goes-here-prod/MAIL_FROM_ADDRESS
    MAIL_FROM_NAME: bref-ssm:/my-real-site-name-goes-here-prod/MAIL_FROM_NAME
    MAIL_MAILER: bref-ssm:/my-real-site-name-goes-here-prod/MAIL_MAILER
    MYSQL_ATTR_SSL_CA: bref-ssm:/my-real-site-name-goes-here-prod/MYSQL_ATTR_SSL_CA
    QUEUE_CONNECTION: sqs
    RAPIDAPI_FOOTBALL_HOST: bref-ssm:/my-real-site-name-goes-here-prod/RAPIDAPI_FOOTBALL_HOST
    RAPIDAPI_NHL_MLB_HOST: bref-ssm:/my-real-site-name-goes-here-prod/RAPIDAPI_NHL_MLB_HOST
    SQS_QUEUE: ${construct:jobs.queueUrl}

  iam:
    role:
      statements:
        - Effect: Allow
          Resource: !GetAtt CacheTable.Arn
          Action:
            - dynamodb:DescribeTable
            - dynamodb:Query
            - dynamodb:Scan
            - dynamodb:GetItem
            - dynamodb:PutItem
            - dynamodb:UpdateItem
            - dynamodb:DeleteItem
        - Effect: Allow
          Action: ssm:GetParameters
          Resource: "arn:aws:ssm:${aws:region}:${aws:accountId}:parameter/*"
        - Effect: Allow
          Action: s3:*
          Resource:
            - !Sub "${Storage.Arn}" # the storage bucket
            - !Sub "${Storage.Arn}/*" # and everything inside

package:
  # Files and directories to exclude from deployment
  patterns:
    - "!node_modules/**"
    - "!public/storage"
    - "!resources/assets/**"
    - "!storage/**"
    - "!tests/**"

functions:
  # This function runs the Laravel website/API
  web:
    handler: public/index.php
    runtime: php-81-fpm
    timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
    events:
      - httpApi: "*"

  # This function lets us run artisan commands in Lambda
  artisan:
    handler: artisan
    runtime: php-81-console
    timeout: 720 # in seconds
    events:
      - schedule:
          rate: rate(1 minute)
          input: '"schedule:run"'

plugins:
  # We need to include the Bref plugin
  - ./vendor/bref/bref
  - serverless-lift

constructs:
  website:
    type: server-side-website
    assets:
      "/build/*": public/build
      "/fonts/*": public/fonts
      "/images/*": public/images
      "/vendor/*": public/vendor
      "/favicon.ico": public/favicon.ico
      "/robots.txt": public/robots.txt
      "/android-chrome-192x192.png": public/android-chrome-192x192.png
      "/android-chrome-512x512.png": public/android-chrome-512x512.png
      "/apple-touch-icon.png": public/apple-touch-icon.png
      "/browserconfig.xml": public/browserconfig.xml
      "/favicon-16x16.png": public/favicon-16x16.png
      "/favicon-32x32.png": public/favicon-32x32.png
      "/mstile-150x150.png": public/mstile-150x150.png
      "/safari-pinned-tab.svg": public/safari-pinned-tab.svg
      "/site.webmanifest": public/site.webmanifest
  jobs:
    type: queue
    worker:
      handler: Bref\LaravelBridge\Queue\QueueHandler
      runtime: php-81
      timeout: 60 # seconds

resources:
  Resources:
    Storage:
      Type: AWS::S3::Bucket
    CacheTable:
      Type: AWS::DynamoDB::Table
      Properties:
        AttributeDefinitions: # only keys are defined here, other attributes are dynamic
          - AttributeName: id # adds a mandatory id field
            AttributeType: S # the type of id is a string
        BillingMode: PAY_PER_REQUEST # billed for each request instead of paying for a constant capacity
        TimeToLiveSpecification: # deletes cache keys automatically based on a ttl field which contains a timestamp
          AttributeName: ttl
          Enabled: true
        KeySchema:
          - AttributeName: id
            KeyType: HASH # the type of key, HASH means partition key (similar to primary keys in SQL)


0 likes
8 replies
LaryAI's avatar
Level 58

The 419 Page Expired error typically occurs when the CSRF token is not being sent or validated correctly. However, since you mentioned that the CSRF token is being sent in the POST request, there might be another issue causing the error.

Here are a few things you can try to troubleshoot the issue:

  1. Verify the CSRF token middleware: Make sure that the VerifyCsrfToken middleware is included in the $middleware property of your app/Http/Kernel.php file. Also, check if the web middleware group is applied to your routes.

  2. Check the session configuration: Ensure that the session driver is set correctly in your .env file. By default, Laravel uses the file driver, but you can also use other drivers like database or redis. Make sure the session driver is compatible with your deployment environment.

  3. Verify the session cookie domain: If you have a custom domain set up, make sure that the SESSION_DOMAIN value in your .env file matches the domain you are using. If you are using a subdomain, include the leading dot (e.g., .example.com).

  4. Check the session cookie path: Ensure that the SESSION_PATH value in your .env file is set correctly. By default, it should be set to /.

  5. Verify the session cookie secure flag: If you are using HTTPS, make sure that the SESSION_SECURE_COOKIE value in your .env file is set to true. This ensures that the session cookie is only sent over secure connections.

  6. Clear cache and regenerate keys: Run the following commands to clear the cache and regenerate the application key:

php artisan cache:clear
php artisan key:generate

If none of the above solutions work, it's possible that there might be an issue specific to the Bref integration. You can try reaching out to the Bref community or checking their documentation for any known issues related to form submissions and CSRF tokens.

Please note that without further information or access to the codebase, it's difficult to provide an exact solution. The suggestions above are general troubleshooting steps that can help identify and resolve common causes of the 419 Page Expired error.

nexxai's avatar
Level 37

@LaryAI Just to respond to Lary's comment for future readers:

  1. I have confirmed that the VerifyCsrfToken middleware is being applied in the $middlewareGroups section of the app/Http/Kernel.php file
  2. I addressed this in my original post, but I have no driver assigned, and so it is initially defaulted to file which is then updated by the BrefServiceProvider to use the cookie type
  3. I do not have a custom domain set up at this time
  4. I have no setting a SESSION_PATH
  5. I have not enabled this flag as I do not believe it is necessary
  6. I have done this multiple times
Snapey's avatar
Snapey
Best Answer
Level 122

no csrf verification is almost always a sign that sessions are not working. Focus on this.

Do you see a session cookie being sent to the client?

nexxai's avatar
Level 37

@Snapey That's what I was leaning toward, but the area that I'm admittedly weakest in.

Where would I check for the cookie being sent?

Snapey's avatar

@nexxai first place to look is in developer tools. In Chrome its in the application tab

nexxai's avatar
Level 37

@snapey Ok, so I force-set the SESSION_DRIVER environment variable to cookie and redeployed.

Now I can see in DevTools that there is a [SITENAME]_session cookie being set with some (I assume) base64 encoded data, but now when I try to submit the form, I'm getting a 500 error.

I'll redeploy the site with it in debug mode to get more data and then report back with what I end up finding.

Thanks for your guidance on this. I think it's helping!

nexxai's avatar
Level 37

Ok,after getting the session stuff figured out, the 500 error was because the migrations had NOT actually run correctly due to having some foreign key constraints on a couple columns which PlanetScale doesn't support. Once I got those figured out, I was totally able to login.

Thanks @snapey again for pointing me in the right direction!

Please or to participate in this conversation.