How to Get Simple URLs When Submitting Forms in Symfony
These are my personal notes on how to simplify a get method form in Symfony
Before you read how to simplify urls on Symfony form submission, let me tell you the use case for making form urls as simple as possible. It's best suited for $_GET requests that you want to be sharable. It's much nicer to send someone to "/myapp.com?name=john" than "/myapp.com?name=john&token=3929203852398502395802398523".
The Problem
Default Symfony form submission produces URLs like this:
https://localhost:8000/date?add_days[start_date]=2026-01-20&add_days[days]=99
By default, Symfony prefixes form field names with the form's name. This is useful when you have multiple forms on the same page, but it makes URLs verbose.
The Goal
A cleaner URL without the add_days[] prefix:
https://localhost:8000/date?start_date=2026-01-20&days=99
The Solution
Instead of using $this->createForm(), inject FormFactoryInterface
and use createNamed() with an empty string as the first argument:
#[Route('/date/{days?}', name: 'app_date_range_add_days')]
public function index(Request $request, FormFactoryInterface $formFactory): Response
{
$form = $formFactory->createNamed('', AddDaysType::class);
// ...
}
The empty string removes the form name prefix from all field names. You can see it in the docs here: Changing the form name
Another way to do it
AFAIK, you can also add this to your form class to do the same thing
public function getBlockPrefix(): string
{
return '';
}
I learned this method from AI. I'm not a fan. This removes the form name from the url every time that form is used.
I think
doing it this way: $form = $formFactory->createNamed('', AddDaysType::class);
is better, because if you were to use $this->createForm(AddDaysType::class) somewhere else,
it would add the name spacing back. Furthermore, the method of using $formfactory->createNamed() is how they do it in the docs. The people who maintain the official docs are excellent programmers.
One more thing: turn off CSRF for shareable GET forms
CSRF tokens protect against attackers tricking a logged-in user into submitting a form that changes state. A GET form that just reads or calculates doesn't change anything, so the token adds nothing and it breaks shareable URLs.
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
// Configure your form options here
'csrf_protection' => false
]);
}
| 'csrf_protection' => true | https://localhost:8000/date?start_date=2026-01-06&days=99&_token=DRYr8p53fZH6mT3NSbd9sTyL&submit= |
| 'csrf_protection' => false | https://localhost:8000/date?start_date=2026-01-06&days=99&submit= |