Type to search columns, filters, options, and extensions.

to navigate · Enter to open · Esc to close

Documentation

Securing Ajax Routes

Protect the bundle's Ajax endpoints with your application's access control

UX DataTables registers a small set of Ajax routes automatically. Server-side tables, server-side exports, the inline edit modal, row deletion, detail rendering, and template resolution all go through these endpoints:

Route namePathMethod(s)Used by
ux_datatables_ajax_data/datatables/ajax/dataGETServer-side data loading
ux_datatables_ajax_delete/datatables/ajax/deleteDELETERow delete action
ux_datatables_ajax_edit/datatables/ajax/editPOST, PATCHInline cell edit
ux_datatables_ajax_edit_form/datatables/ajax/edit-form/viewPOSTEdit modal form
ux_datatables_ajax_edit_form_submit/datatables/ajax/edit-formPOSTEdit modal submit
ux_datatables_ajax_detail/datatables/ajax/detailPOSTRow detail rendering
ux_datatables_ajax_templates/datatables/ajax/templatesPOSTTemplate column rendering
ux_datatables_ajax_export/datatables/ajax/exportPOSTServer-side CSV/XLSX export

Why this matters

Each rendered table carries a signed token (an HMAC of the DataTable class name, derived from your application’s kernel.secret). The bundle uses this token to resolve the correct AbstractDataTable service on the server. The token is stable, it is visible in the page source, and it does not encode anything about the current user.

This means that if a table is displayed on a page behind an admin firewall, but the global /datatables/ajax/* routes are not covered by an equivalent access control rule, the underlying data (and the edit/delete actions) can be reached by anyone who obtains the token — including a user who is not allowed to view the page that renders the table.

The bundle cannot know how your application authenticates and authorizes users, so it deliberately does not enforce anything at the route level. Securing these routes is the responsibility of the host application.

Protect the routes with access_control

The simplest and most robust approach is to add an access_control rule that scopes the shared ^/datatables/ajax path prefix to match the access requirements of the pages that render your tables. Add it to config/packages/security.yaml:

# config/packages/security.yaml
security:
    # ...

    access_control:
        # Restrict every UX DataTables Ajax endpoint to authenticated admins.
        # Match this to whatever the pages rendering your tables require.
        - { path: ^/datatables/ajax, roles: ROLE_ADMIN }

        # ... your other rules

If different tables live behind different access levels, use more specific rules or place the tables (and their Ajax traffic) behind the same firewall so the same authorization applies to both the page and the data endpoint.

Verifying your configuration

After adding the rule, confirm it is active:

# List the registered UX DataTables routes
php bin/console debug:router | grep ux_datatables

# Check which access_control rule matches the Ajax prefix
php bin/console debug:firewall

Then, as an unauthenticated (or under-privileged) user, request /datatables/ajax/data directly and confirm the firewall responds with a redirect to login or a 403/401 instead of returning table data.

Session-backed mutations

Delete actions and inline boolean toggles also require an active session so the bundle can create and validate a CSRF token. When rendering happens without a session (for example behind a stateless firewall), the table payload exposes mutationsEnabled: false; delete buttons and boolean switches are rendered disabled instead of sending requests that can only fail with 403.

Use these mutation controls only on session-backed pages. A stateless application should provide its own authenticated mutation endpoints and controls rather than relying on the bundle’s session-backed delete and toggle endpoints.

Authorizing tables, actions, and rows

access_control protects the route — it decides who may call /datatables/ajax/* at all. It does not know anything about the specific table, action, or row a request targets. The bundle closes that gap by calling Symfony authorization before rendering a table, executing an action, reading private row details, or mutating anything.

That second layer has its own guide: Authorization and Voters covers the Permission::DT_* attributes, the row permission matrix, and how to write the voters your tables need.

HTML Escaping Checklist

Twig auto-escaping stays enabled for bundle templates. Treat HTML cell content as a security boundary:

  • Prefer typed columns (TextColumn, MoneyColumn, …) that render escaped scalars.
  • Use TemplateColumn only with templates you control. Escape untrusted entity fields inside the Twig template ({{ value }}, never {{ value|raw }} unless the value is already sanitized).
  • Do not put user-controlled HTML into defaultContent or action labels without escaping.
  • Custom JS render callbacks must escape when inserting into the DOM.

Production Checklist

  1. access_control covers ^/datatables/ajax with the same roles as the pages that render tables.
  2. Session-backed pages for delete / boolean toggle / edit modal (CSRF).
  3. Voters for Permission::DT_EDIT_ROW, Permission::DT_DELETE_ROW, and Permission::DT_VIEW_ROW_DETAILS where those row features are enabled.
  4. Template/HTML columns reviewed for escaping (see above).
  5. Unauthenticated probe of /datatables/ajax/data returns 401/403, not table JSON.