Troubleshooting Form Submission Performance

Introduction

The performance of form submissions can be an important factor in the user experience of your site, so it is essential that you understand how the process works. Factors that can impact performance include:

  • The browser, device, and internet connection of the user submitting the form.
  • The network between the user’s device and the server hosting your site.
  • The available resources on the server hosting your site.
  • The site database (MySQL or MariaDB) version and configuration.
  • PHP version and configuration.
  • WordPress version.
  • The active theme (and child theme).
  • The active plugins.
  • The number of users visiting the site around the time of the submission.
  • The number of users submitting forms around the same time.
  • Form complexity: the number of fields, pages, conditional logic, and calculations.
  • Integrations: the performance of other services the form integrates with, such as email marketing, CRMs, and payment gateways.

The rest of this article focuses on how to troubleshoot the Gravity Forms part of the process.

Gravity Forms is just one part of your site. WordPress, the theme, and other plugins will also be running their own processes before, during, and after the form submission processes described below.

Getting Started

Begin troubleshooting by:

  • Enabling logging on the Forms > Settings page. Don’t forget to save the settings once logging is enabled.
  • On the Forms > Settings > Logging page, ensure that logging is enabled for Gravity Forms Core and all add-ons.
  • Go to the page where your form is embedded, fill and submit the form so the logging statements and their timings are recorded.
  • View the logs via the Forms > Settings > Logging or Forms > System Status pages.

Check our logging and debugging documentation for additional help.

Reviewing the Core Log

The example logging statements found below are just a few of the key events to look out for. Some of them are specific to Gravity Forms 2.7 and greater. The logs can contain many more logging statements than are documented here.

To narrow down where any bottlenecks are located in the form submission process, you would compare the date and time portion at the start of each logging statement. This uses the format: Year-Month-Day Hour:Minutes:Seconds.Microseconds e.g. 2023-01-16 16:26:31.894715.

Processing Starts

When viewing the core log, the first logging statement recorded for a submission is from GFFormDisplay::process_form(), it identifies the ID of the form being processed.

[date and time] - DEBUG --> GFFormDisplay::process_form(): Starting to process form (#[Form ID]) submission.

This is followed by a logging statement that identifies the source and target page numbers.

[date and time] - DEBUG --> GFFormDisplay::process_form(): Source page number: 1. Target page number: 0. 

A target page number of 0 indicates the form was submitted with the intention of saving the entry, if the submission is valid.

Validation

Performance during validation can be impacted by the number of fields on the form, calculations, conditional logic, and add-ons that integrate using the field or form validation filters, such as payment add-ons communicating with payment gateways.

Once the form, source, and target page numbers are identified, validation can begin. It starts by validating any restrictions that have been configured in the form settings, such as the schedule and entry limits.

[date and time] - DEBUG --> GFFormDisplay::validate(): Starting for form #[Form ID].
[date and time] - DEBUG --> GFFormDisplay::validate(): Checking restrictions.
[date and time] - DEBUG --> GFFormDisplay::validate(): Completed restrictions. Starting field validation.

Once those checks are complete, the fields are validated.

[date and time] - DEBUG --> Field validation completed in [number] seconds.

After field validation is completed, integrations, such as payment add-ons, can run checks using the gform_validation filter.

[date and time] - DEBUG --> Executing functions hooked to gform_validation.
[date and time] - DEBUG --> Completed gform_validation.

Finally, the validation result is returned, so submission can continue.

[date and time] - DEBUG --> GFFormDisplay::process_form(): After validation. Is submission valid? Yes. 
[date and time] - DEBUG --> GFFormDisplay::process_form(): Submission is valid. Moving forward.

Saving the Entry

When the form is valid, the next step is saving the entry.

Performance during entry save can be impacted by the number of fields on the form, calculations, conditional logic, and the server(s) hosting the site and WordPress database.

The first stage of saving the entry is adding the entry properties, such as the current date and time, to the gf_entry table.

[date and time] - DEBUG --> GFFormsModel::save_entry(): Saving entry. 
[date and time] - DEBUG --> GFFormsModel::save_entry(): Entry record created in the database. ID: [Entry ID]. 
[date and time] - DEBUG --> GFFormsModel::save_entry(): Saving entry fields. 

Once the WordPress database has returned the ID it assigned the new entry, the field values can be queued for batch saving to the gf_entry_meta table.

The label, ID, and type of each field being saved will be recorded in the log.

[date and time] - DEBUG --> GFFormsModel::queue_save_input_value(): Queued field operation: [Field Label](#[Field ID] - [Field or Input Type]). 

Once the values have been batch saved to the database, you’ll find the following logging statements.

[date and time] - DEBUG --> GFFormsModel::save_entry(): Finished saving entry fields.
[date and time] - DEBUG --> GFFormsModel::save_entry(): Saving entry completed in [number] seconds

Next up is the saving of entry meta for add-ons.

[date and time] - DEBUG --> GFFormsModel::set_entry_meta(): Saving meta for entry (#[Entry ID]) completed in [number] seconds.

Triggering Integrations

Next up are the configured integrations, starting with anti-spam checks.

Performance at this stage of the submission can be impacted by the number of integrations that are configured for the form, the number of feeds using conditional logic, and the performance of the services add-ons integrate with.

[date and time] - DEBUG --> GFCommon::is_spam_entry(): Executing functions hooked to gform_entry_is_spam. 
[date and time] - DEBUG --> GFCommon::is_spam_entry(): Result from gform_entry_is_spam filter: false 
[date and time] - DEBUG --> GFCommon::is_spam_entry(): Spam checks completed in [number] seconds. Is submission considered spam? No.

Integrations such as add-on feeds are then processed. Most add-ons won’t process an entry that has been identified as spam.

[date and time] - DEBUG --> GFFormDisplay::handle_submission(): Executing functions hooked to gform_entry_created. 
[date and time] - DEBUG --> GFFormDisplay::handle_submission(): Completed gform_entry_created.
[date and time] - DEBUG --> GFFormDisplay::handle_submission(): Executing functions hooked to gform_entry_post_save. 
[date and time] - DEBUG --> GFFormDisplay::handle_submission(): Completed gform_entry_post_save.
[date and time] - DEBUG --> GF_Background_Process::dispatch(): Running for gf_feed_processor.

Each add-on has a log file recording how it processed the submission, so you’ll also want to review the timings of the logging statements in the add-on logs.

Legacy Post Creation

Next up is the built-in legacy post creation functionality.

Performance at this stage of the submission can be impacted by the number of fields on the form and the number of file uploads to be added to the media library.

Forms without legacy Post fields

Even if your form isn’t using the legacy post fields, you’ll find the following in the log:

[date and time] - DEBUG --> GFFormsModel::create_post(): Starting. 
[date and time] - DEBUG --> GFFormsModel::create_post(): Stopping. The form doesn't have any post fields. 

Forms with legacy Post fields

Create the Post

The first stage of post creation is to get the data from all the Post fields and create the initial post object.

[date and time] - DEBUG --> GFFormsModel::create_post(): Starting.
[date and time] - DEBUG --> GFFormsModel::create_post(): Getting post fields.
[date and time] - DEBUG --> GFFormsModel::create_post(): Inserting post via wp_insert_post().
[date and time] - DEBUG --> GFFormsModel::create_post(): Result from wp_insert_post(): [Post ID]
Process Media

If the form has any Post Image fields, their files are copied to the media library and attached to the post.

[date and time] - DEBUG --> GFFormsModel::create_post(): Processing post images.
[date and time] - DEBUG --> GFFormsModel::create_post(): No image to process for field #[Field ID]
[date and time] - DEBUG --> GFFormsModel::create_post(): Field #[Field ID]. URL: [File URL]
[date and time] - DEBUG --> GFFormsModel::media_handle_upload(): Image copied to the media directory. Result from wp_insert_attachment(): [Attachment ID]

If the Set as Featured Image setting is enabled on the field, you’ll also find the following:

[date and time] - DEBUG --> GFFormsModel::create_post(): Setting the featured image. Result from set_post_thumbnail(): [bool]
Add Custom Fields

Next, if the form has any Post Custom Field‘s, their values will be added to the post meta.

[date and time] - DEBUG --> GFFormsModel::create_post(): Adding custom fields.
[date and time] - DEBUG --> GFFormsModel::create_post(): Getting custom field: [Meta Key]
Process Templates

If the Create Content Template setting is being used on any of the Post fields, you’ll then find logging showing the content templates being processed and the post being updated.

[date and time] - DEBUG --> GFFormsModel::process_post_template(): Processing post_content template.
[date and time] - DEBUG --> GFFormsModel::create_post(): Updating post.
Update Entry

The final step of the legacy post creation feature is updating the entry with the ID of the created post.

[date and time] - DEBUG --> GFFormsModel::create_post(): Updating entry with post id.

Triggering Notifications

The next stage in the form submission process is the triggering of notifications assigned to the default Form is submitted event. This is where most delays occur.

Performance at this stage of the submission can be impacted by the number of notifications, their use of routing and conditional logic, and the attaching of uploaded files. It can also be impacted by the configuration of WordPress, the server hosting the site, and SMTP/email service plugins.

2023-01-16 16:26:31.894715 - DEBUG --> GFAPI::send_notifications(): Gathering notifications for form_submission event for entry #[Entry ID]. 
2023-01-16 16:26:31.894812 - DEBUG --> GFCommon::send_notifications(): Processing notifications for form_submission event for entry #[Entry ID]: Array
(
    [0] => [Notification ID]
)
 
(only active/applicable notifications are sent) 
2023-01-16 16:26:31.894885 - DEBUG --> GFCommon::send_notification(): Starting to process notification (#[Notification ID] - [Notification Name]). 
2023-01-16 16:26:31.895328 - DEBUG --> GFCommon::send_email(): Sending email via wp_mail(). 
2023-01-16 16:26:31.895404 - DEBUG --> [email details removed from log]
2023-01-16 16:26:46.920707 - DEBUG --> GFCommon::send_email(): Result from wp_mail(): 1 
2023-01-16 16:26:46.920818 - DEBUG --> GFCommon::send_email(): WordPress successfully passed the notification email (#[Notification ID] - [Notification Name]) for entry #[Entry ID] to the sending server.
2023-01-16 16:26:46.923607 - DEBUG --> GFCommon::send_notifications(): Sending notifications for entry (#[Entry ID]) completed in 15.021593 seconds.

In this example, you can see that it took WordPress and the server hosting the site 15 to 16 seconds to send the email and pass back the result.

That’s a substantial delay before the user sees the form confirmation, which would be even longer if the form had multiple notifications assigned to be sent on form submission.

Preparing the Confirmation

After triggering notifications, the form confirmation is prepared for output.

Performance in this final stage of submission processing can be impacted by the number of confirmations and their use of conditional logic.

For forms with multiple confirmations, it will loop through them, testing the conditional logic to determine which should be used for the current entry. If none of the conditional logic rules matched, it will fall back to the default confirmation.

[date and time] - DEBUG --> GFFormDisplay::handle_confirmation(): Preparing confirmation (#[Confirmation ID] - [Confirmation Name]). 

If the theme or another plugin is using the gform_confirmation filter, such as payment add-ons that redirect to hosted payment pages, you’ll also find the following logging statements.

[date and time] - DEBUG --> GFFormDisplay::handle_confirmation(): Executing functions hooked to gform_confirmation.
[date and time] - DEBUG --> GFFormDisplay::handle_confirmation(): Completed gform_confirmation.

We then log the confirmation message or redirect that will be used.

[date and time] - DEBUG --> GFFormDisplay::handle_confirmation(): Confirmation to be used => <div id='gform_confirmation_wrapper_740' class='gform_confirmation_wrapper '><div id='gform_confirmation_message_740' class='gform_confirmation_message_740 gform_confirmation_message'>Thanks for contacting us! We will get in touch with you shortly.</div></div> 

Processing Completes

If the theme or another plugin is using the gform_after_submission hook, you’ll also find the following:

[date and time] - DEBUG --> GFFormDisplay::process_form(): Executing functions hooked to gform_after_submission.
[date and time] - DEBUG --> GFFormDisplay::process_form(): Completed gform_after_submission.

And here’s the last logging statement to be output by GFFormDisplay::process_form(), it contains the total run time for that method.

[date and time] - DEBUG --> GFFormDisplay::process_form(): Processing completed in [number] seconds.

All of the above occurs before WordPress, the theme, and other active plugins determine which template to use to render the requested page.

As WordPress is preparing to output the scripts for the page, you might find the following, which indicates the scripts to prevent duplicate submissions are being queued for output:

[date and time] - DEBUG --> Gravity_Forms\Gravity_Forms\Duplicate_Submissions\GF_Duplicate_Submissions_Handler::is_valid_submission(): form #[Form ID]. entry #[Entry ID].

Finally, as WordPress is processing shortcodes and blocks in the content of the page to be displayed, you might find the following:

[date and time] - DEBUG --> GFFormDisplay::get_form(): Preparing form (#[Form ID]) confirmation completed in [number] seconds.

Improving Performance

Here are some ways to improve form submission performance for your users.

Updates

Make sure your site is using the latest versions of WordPress, the theme, and all plugins. See The Importance of Updates.

It’s also important to keep the packages on the server hosting the site updated. Older versions of PHP, cURL, and OpenSSL can be slow and insecure.

Number of Fields

Breaking up a large form into two or more smaller forms is one of the most efficient ways to improve form performance. This can have the following benefits:

  • Smaller forms are easier for form administrators to maintain.
  • Forms with fewer fields are easier to use and less intimidating for end users.
  • The number of fields to validate is reduced.
  • There will be fewer calculations and conditional logic to evaluate.
  • Fewer fields to process during merge tag and add-on feed processing.

Add-ons that can help with this include:

  • Bulk Actions, by JetSloth, enables bulk management of fields.
  • Easy Passthrough, by Gravity Wiz, can be used to securely pass values between forms without exposing them in the confirmation query string.
  • Gravity Flow and the Form Connector extension can be used to capture additional values later in a workflow. For more details, see the Optimize initial form submission page of the Gravity Flow documentation.
  • Nested Forms, by Gravity Wiz, adds a new field type, called a Nested Form field, which allows you to embed a “child” form into a “parent” form. This child form is used to collect child entries and attach them to the parent entry on submission. The child entries display in a clean, compact table on the parent form where they can be viewed, edited or deleted.

Page Fields

When considering how Page fields can be used to break up forms into smaller sections, it’s worth remembering the following:

  • Only the fields on the current page are validated when using the Next button.
  • Validation is not performed when returning to the previous page.
  • Fields on all pages are validated when submitting the form.
  • Pages and fields hidden by conditional logic are not validated.

Paging performance of some forms can be improved by using Page Transitions, by Gravity Wiz. This add-on can prevent submission of individual pages, so that server-side validation only occurs when the form is submitted from the last page.

Conditional Logic

Evaluating conditional logic can be time-consuming for forms with many fields. You can improve performance by reducing the number of fields using conditional logic.

Instead of configuring the same rules on multiple fields that are grouped together, configure them on Page or Section fields.

All fields located after a Section field, up to the next Section or Page field, are displayed or hidden with the Section field.

If this doesn’t help, consider the solutions from Number of Fields above.

File Uploads

The standard File Upload field, which uses the browsers default file input to upload a single file, can impact form submission performance. This is because the browser only starts the upload when the user submits the form. The larger the file, the longer the user will have to wait for the submission to complete.

You can improve performance by using a File Upload field with the Multi-File Upload feature enabled in the field settings. This will use Ajax to upload the file as soon as the user selects it.

You can limit the Multi-File Upload enabled field, so only one file can be uploaded, by configuring the Maximum Number of Files setting in the field settings.

No Duplicates

Try to limit how many fields the “No Duplicates” setting is enabled on.

When enabled, field validation will query the database to ensure the submitted value hasn’t already been used by an existing entry for that field.

Form submission performance could be impacted if many fields have the setting enabled.

Integrations

Some add-ons can process their feeds in the background, so they don’t slow down form submission completion. For more details, see Add-On Support for Background Feed Processing.

Alternatively, Gravity Flow can be used to schedule the feed processing of compatible add-ons for after the form submission has completed. A list of supported add-ons is available on the Integration with Gravity Forms Add-Ons page of the Gravity Flow documentation.

If you are using Gravity PDF, you can turn on background processing to stop PDF generation from affecting form submission when notifications are sent.

Post Creation

We recommend using the Advanced Post Creation Add-On instead of the built-in legacy post fields. Its feeds can be processed in the background, so it doesn’t block the submission.

The Gravity Flow Create Post Step can be scheduled to process APC feeds after submission has completed.

Notifications

Disabled by default, background processing of notifications allows the user to see the confirmation faster by moving the sending of notifications to a separate request.

Gravity PDF, by Blue Liquid Designs, includes built-in support for background processing of notifications that include PDFs. It can be enabled on the Forms > Settings > PDF page.

For sites or forms not using Gravity PDF, the gform_is_asynchronous_notifications_enabled filter can be used to enable it (requires Gravity Forms 2.6.9 or higher).

The following add-ons can be used to delay form submission notifications, schedule notifications, send reminder notifications, create drip-style marking campaigns, and more.

Further Reading

Disclaimer: Third-party services, plugins, or code snippets that are referenced by our Support documentation or in Support Team communications are provided as suggestions only. We do not evaluate, test or officially support third-party solutions. You are wholly responsible for determining if any suggestion given is sufficient to meet the functional, security, legal, ongoing cost and support needs of your project.

Feedback, feature and integration requests, and other functionality ideas can be submitted on our Gravity FormsGravity Flow, or Gravity SMTP product roadmap pages.