Preventing duplicate form submissions - php

I came up with a technique to prevent duplicate form submission by going back/forward or refreshing the page. And I thought about duscussing it here, I already tested a sample not in production environment, what is flaws that you can identify?
Please note that I am well aware of using Form Tokens, which will defend you against CSRF attacks, and wasn't added in the steps below.
-Generate Form ID for each form, and use it as hidden field in the form:
$formid = microtime(true)*10000;
-On form submit:
Validate from data
Calculate the hash of form fields data
$allvals = '';
foreach($_POST as $k=>$v){
$allvals .= $v;
}
$formHash = sha1($allvals);
Validate form hash by comparing with previously saved hashes. the session value is binded to each form by $formid variable.
$allowAction = true;
if(isset($_SESSION['formHash'][$_POST['formid']]) && ($_SESSION['formHash'][$_POST['formid']] == $formHash)){
$allowAction = false;
}
if form hash wasn't found, it means this is the first time form submitted or the form data is changed.
If data saved ( to database, for example), save form hash to session:
$_SESSION['formHash'][$_POST['formid']] = $formHash;
Full version of the code:
http://thebusy.me/2011/01/06/preventing-duplicate-form-submissions/

A simpler way to achieve what you want is to use redirect on submit. After you process a POST request you redirect, possibly even to the same page. This is a common pattern called "Redirect after POST" or POST/Redirect/GET.
For example:
<?php
if($_POST) {
// do something
// now redirect
header("Location: " . $_SERVER["REQUEST_URI"]);
exit;
}
?>
<html> ...
<form method="post" action=""> ... </form>
By setting the action to "" then it will submit to itself, at which point the if($_POST) code block will validate to true and process the form, then redirect back to itself.
Of course you probably want to redirect to a different page that shows a "your form has been submitted" response or put the form on a different page and have the HTML of this page be the response.
The benefit of this method is that when you hit the back button it does a GET request so the form is not re-submitted.
On Firefox, it will actually take the submission to itself out of the browser history so when users browse across the web and then hit back, instead of seeing the "thank you" page they see the form page.

It looks like you are getting overly complicated with this. My favorite way, because it also prevents some session jacking hacks at the same time, is described here:
http://www.spotlesswebdesign.com/blog.php?id=11
It's simple and easy to impliment on any form. It uses a randomly generated page instance id to verify that the form submission received is identical to the last page served to that particular user.

Both solutions above are good but a bit short.
how about stopping further insertions in the next few minutes from the same user with perhaps minor changes in data?
this can be done by putting an md5 hash in a cookie on the users machine and storing a copy in the database - this way any further attempt from the same machine over a specified time can be ignored and stopped from being inserted into the database.
perhaps someone can comment on the validity and effectiveness of my suggestion or am i barking up the wrong tree ???

Related

how can i get rid of confirm form resubmission in HTML/PHP? [duplicate]

Page one contains an HTML form. Page two - the code that handles the submitted data.
The form in page one gets submitted. The browser gets redirected to page two. Page two handles the submitted data.
At this point, if page two gets refreshed, a "Confirm Form Resubmission" alert pops up.
Can this be prevented?
There are 2 approaches people used to take here:
Method 1: Use AJAX + Redirect
This way you post your form in the background using JQuery or something similar to Page2, while the user still sees page1 displayed. Upon successful posting, you redirect the browser to Page2.
Method 2: Post + Redirect to self
This is a common technique on forums. Form on Page1 posts the data to Page2, Page2 processes the data and does what needs to be done, and then it does a HTTP redirect on itself. This way the last "action" the browser remembers is a simple GET on page2, so the form is not being resubmitted upon F5.
You need to use PRG - Post/Redirect/Get pattern and you have just implemented the P of PRG. You need to Redirect. (Now days you do not need redirection at all. See this)
PRG is a web development design pattern that prevents some duplicate form submissions which means, Submit form (Post Request 1) -> Redirect -> Get (Request 2)
Under the hood
Redirect status code - HTTP 1.0 with HTTP 302 or HTTP 1.1 with HTTP 303
An HTTP response with redirect status code will additionally provide a URL in the location header field. The user agent (e.g. a web browser) is invited by a response with this code to make a second, otherwise identical, request to the new URL specified in the location field.
The redirect status code is to ensure that in this situation, the web user's browser can safely refresh the server response without causing the initial HTTP POST request to be resubmitted.
Double Submit Problem
Post/Redirect/Get Solution
Source
Directly, you can't, and that's a good thing. The browser's alert is there for a reason. This thread should answer your question:
Prevent Back button from showing POST confirmation alert
Two key workarounds suggested were the PRG pattern, and an AJAX submit followed by a scripting relocation.
Note that if your method allows for a GET and not a POST submission method, then that would both solve the problem and better fit with convention. Those solutions are provided on the assumption you want/need to POST data.
The only way to be 100% sure the same form never gets submitted twice is to embed a unique identifier in each one you issue and track which ones have been submitted at the server. The pitfall there is that if the user backs up to the page where the form was and enters new data, the same form won't work.
There are two parts to the answer:
Ensure duplicate posts don't mess with your data on the server side. To do this, embed a unique identifier in the post so that you can reject subsequent requests server side. This pattern is called Idempotent Receiver in messaging terms.
Ensure the user isn't bothered by the possibility of duplicate submits by both
redirecting to a GET after the POST (POST redirect GET pattern)
disabling the button using javascript
Nothing you do under 2. will totally prevent duplicate submits. People can click very fast and hackers can post anyway. You always need 1. if you want to be absolutely sure there are no duplicates.
You can use replaceState method of JQuery:
<script>
$(document).ready(function(){
window.history.replaceState('','',window.location.href)
});
</script>
This is the most elegant way to prevent data again after submission due to post back.
Hope this helps.
If you refresh a page with POST data, the browser will confirm your resubmission. If you use GET data, the message will not be displayed. You could also have the second page, after saving the submission, redirect to a third page with no data.
Well I found nobody mentioned this trick.
Without redirection, you can still prevent the form confirmation when refresh.
By default, form code is like this:
<form method="post" action="test.php">
now, change it to
<form method="post" action="test.php?nonsense=1">
You will see the magic.
I guess its because browsers won't trigger the confirmation alert popup if it gets a GET method (query string) in the url.
The PRG pattern can only prevent the resubmission caused by page refreshing. This is not a 100% safe measure.
Usually, I will take actions below to prevent resubmission:
Client Side - Use javascript to prevent duplicate clicks on a button which will trigger form submission. You can just disable the button after the first click.
Server Side - I will calculate a hash on the submitted parameters and save that hash in session or database, so when the duplicated submission was received we can detect the duplication then proper response to the client. However, you can manage to generate a hash at the client side.
In most of the occasions, these measures can help to prevent resubmission.
I really like #Angelin's answer. But if you're dealing with some legacy code where this is not practical, this technique might work for you.
At the top of the file
// Protect against resubmits
if (empty($_POST)) {
$_POST['last_pos_sub'] = time();
} else {
if (isset($_POST['last_pos_sub'])){
if ($_POST['last_pos_sub'] == $_SESSION['curr_pos_sub']) {
redirect back to the file so POST data is not preserved
}
$_SESSION['curr_pos_sub'] = $_POST['last_pos_sub'];
}
}
Then at the end of the form, stick in last_pos_sub as follows:
<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>
Try tris:
function prevent_multi_submit($excl = "validator") {
$string = "";
foreach ($_POST as $key => $val) {
// this test is to exclude a single variable, f.e. a captcha value
if ($key != $excl) {
$string .= $key . $val;
}
}
if (isset($_SESSION['last'])) {
if ($_SESSION['last'] === md5($string)) {
return false;
} else {
$_SESSION['last'] = md5($string);
return true;
}
} else {
$_SESSION['last'] = md5($string);
return true;
}
}
How to use / example:
if (isset($_POST)) {
if ($_POST['field'] != "") { // place here the form validation and other controls
if (prevent_multi_submit()) { // use the function before you call the database or etc
mysql_query("INSERT INTO table..."); // or send a mail like...
mail($mailto, $sub, $body); // etc
} else {
echo "The form is already processed";
}
} else {
// your error about invalid fields
}
}
Font: https://www.tutdepot.com/prevent-multiple-form-submission/
use js to prevent add data:
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}

navigate back with PHP form submission

So here is the deal,
I am using HTML forms to transfer variables from page to page and PHP script to create pages based on values submitted.
In general it looks like this: from the catalog of items you select what you want and the next page shows details for this specific item. Everything works perfect, except one thing:
Whenever I use browser's back button, I always get the error: ERR_CACHE_MISS and I need to refresh page and then confirm that I really want to resubmit data.
Is there any way to fix this, so my customers would be able just to use back button as they supposed to.
Here is the full text that browser provides me:
This webpage requires data that you entered earlier in order to be
properly displayed. You can send this data again, but by doing so
you will repeat any action this page previously performed. Reload this
webpage. Press the reload button to resubmit the data needed to load
the page. Error code: ERR_CACHE_MISS
When you post forms with php, or any other data, you may come back to the page and find a message in the browser like "Document Expired" or "Confirm Form Resubmission With Chrome". These messages are a safety precaution the browser uses with sensitive data such as post variables. The browser will not automatically give you the fresh page again. You must reload the page by clicking try again or with a page refresh. Then, it operates as you would expect it to.
However, the php coder can work around the annoying message from the browser by adding a little code into the script. The example shows a couple of lines of code that can be added above session_start() in order to be able to go back and forth to the page when you post without any hangups.The 'private_no_expire' mode means that the client will not receive the expired header in the first place.
header('Cache-Control: no cache'); //no cache
session_cache_limiter('private_no_expire'); // works
//session_cache_limiter('public'); // works too
session_start();
**Some background: Credit goes to bruce (sqlwork.com) for his excellent explanation.
This web page requires data that you entered earlier in order to be properly displayed. You can send this data again, but by doing so you will repeat any action this page previously performed. Press Reload to resend that data and display this page.
Because of the sloppy coding practices of web developers browsers were forced to add this message. the scenario is as follows:
1) user fills in form and submits (posts form)
2) the server process the post data and responds with a new page (confirm) marked as not cacheable
3) the user navigates to a new page.
4) the user press back:
for the the browser to display the page in step 2, because its marked no-cache, it must request it from the server, in other words do the repost of the data (do step 1). here is were the sloppy coding came in, if this was an credit card charge, and repost detection was not on the server, the card is charged twice. this was (is) so common a problem, that the browsers had to detect this and warn the users.
the best fix is in step two, the server sends a redirect to the confirm page. then when the user accesses the confirm via history or back, its a get request, not a post request and will not show the warning.
note: webform's postback model lends itself to this problem. also avoid server transfers.
My solution
$_SESSION['home'] used to store any errors on home page.
$_SESSION['tempEmail'] used to echo value on php form.
Note: Use one unique session variable for each page that has a HTML form for error handling and also any session variable for each value that is echoed on HTML form.
<?php
session_start();
//Initialize variables not initialized without overwriting previously set variables.
if(!isset($_SESSION['home'])) {
$_SESSION['home']="";
$_SESSION['tempEmail']="";
}
Optional - If logged in, assign email address to the $_SESSION['tempEmail'] variable (if not previously done) to pre-fill HTML form.
if(isset($_POST['Submit'])){
---your code---
//Error message(s) examples
$_SESSION['home'] = "Email and Password do not match, please try again.";
header("Location: " . $_SERVER['REQUEST_URI']);
$_SESSION['home'] = "Email address format is invalid. Please recheck.";
header("Location: " . $_SERVER['REQUEST_URI']);
//success
unset ($_SESSION['home']); //optional, unset to clear form values.
header ("location: nextpage.php");
---or---
header("Location: " . $_SERVER['REQUEST_URI']); //re-post to same page with the $_SESSION['home'] success message.
}
?>
<body>
Error box
<span><strong class="error"><?php echo $_SESSION['home'] ?></strong></span>
HTML form
<form action="#" name="loginform" method="post" >
<input type="text" name="userEmail" maxlength="50" title="Enter Your email" autocomplete="off" value="<?php echo htmlspecialchars($_SESSION['tempEmail']); ?>" placeholder="enter email" required/>
<input type="submit" name="Submit" value="Submit">
</form>
</body>
Not recommended to use on payment page,see discussion above. Tested in Firefox, Chrome, Safari, and IE9. The annoying messages are gone when using back button. Ensure that output buffering is turned "on" in your php script or php.ini to avoid header warnings. You can check your php.ini file for the following;
output_buffering=On
I found that using just :
header('Cache-Control: no cache'); //disable validation of form by the browser
resolve the problem
None of the other answers worked for me.
I don't want to redirect
Setting different headers didn't work
I already use tokens in my post to ensure re-submission can't happen
I post to the same url the form is showing on
This simple javascript fixes my issue of the back button throwing "ERR_CACHE_MISS"
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
I tried this answer and it's ok.
You have to put this code before: session_start():
session_cache_limiter('private, must-revalidate');
session_cache_expire(60);
Good luck
<?php
if(isset($_POST['submit']))
{
//submission goes here
}
?>
Is this what you were thinking?
edit - SQL really is a beautiful thing to work with, I see it's been added as a recommendation in a comment, and I concur to use SQL if you can, its fast, intuitive and efficient.

Reset URL on form submit

I have a form that acts as a filter to a list of inventory.
The form works well but I have been using get in order for the user to flip through pages, for example:
Next page
<?php } ?>
I am getting my data from XML and this is the way I've found works best. However, the form to filter is POST and if a user clicks next page and tries to use the filter afterwards(bunch of drop boxes) then It also uses the get-parameters that have been passed to the URL from the link.
Is there a way, that no form submit, It will reset all the parameters?
http://www.website.com/used-cars/?pos=10&q=Model-Corolla%2C&srt=KMDfr
That would be preform submission, and once the form is submitted, it will look like this:
http://www.website.com/used-cars/
And there will be no GET variables for the page to get anymore.
Yes, after you're done with your processing, call
header("Location: /used-cars/");
die();
And it will redirect the user to the wanted page.
First of all, don't just use the default header("Location...") alone, because that would send a 302 Found (previously called: Moved Temporarily) response, which kinda "lies" about the actual behavior (as it still means "The requested resource resides temporarily under a different URI"). Worse yet: if a form uses POST (which most do), a conforming browser should even ask for permission before redirecting, according to HTTP 1.1.
So, to properly reset form URIs with a GET, use 303 See Other instead, which was specifically added for this purpose.
(It's nice to also combine it with a 201 Created response intended to ack. successful form submissions, so adding a
header("HTTP/2.0 201 Created") to the result page is a nice touch.)
But, to address your old comment "Where would I add this php code? I'm not sure where to put it, the form submission reloads the current page." (though you probably figured it out since then ;) ):
You'll have to handle not only two, but at least three, or even four cases (in conditional branches):
You send the form for displaying + submitting.
You receive the the form data, store it somewhere (i.e. "create a new resource", the idea behind 201 Created), and redirect to a clean URI.
To avoid redisplaying the form again as if nothing had happened (or redirecting forever to the same page), you must detect if you have just redirected to yourself...
But, since you've now removed all the inputs from the URI, you must use some other means to keep track of state. Some straightforward ways for that:
a) Redirect to a different URI.
b) Use a PHP session.
And, finally, if needed: reset and display the form again for new inputs.
Here's an example (with 3/b, and a kind of "faked" 4, for simplicity):
session_start();
if (isset($_GET['some_input'])) // Case 2: We got data!
{
file_put_contents("result", $_GET['some_input']);
$_SESSION['redir'] = true;
header("Location: /used-cars/", true, 303);
exit;
}
else if (isset($_SESSION['redir'])) // Case 3: We have redirected!
{
unset($_SESSION['redir']);
http_response_code(201); // Acknowledge receiving the form data.
echo "OK, we have happily processed the last submitted data: ",
file_get_contents("result"), "<br>";
echo "Reload the page to fill the form again!"; // Case 4: Reset...
}
else // Case 1: Send the form...
{
echo <<<_
<form>
<input type="text" name="some_input">
<input type="submit">
</form>
_;
}

How to show 'success' message on form submit without changing URL?

I have a PHP site (with CodeIgniter) that includes a registration form. I have a page with some details, which links to the form on a separate page. The form posts to a third URL which does the processing and redirects back to the first page if it's successful (or the form page if not).
Currently I am adding a parameter for success: example.com/page?success=1 which shows a success message. The problem is that some people have been sharing this URL (and clicking the Facebook Like button) so when another user opens that URL they see a message "thanks for registering!" which they obviously haven't done yet.
I thought this was the standard way of doing forms (submitting to one URL and redirecting to another) but is there a better way? I don't want to post back to the same page because then you get the POSTDATA warning when trying to reload the page.
You have three ways to do this
The way you're using
Not actually redirecting but sending request(s) with AJAX
SESSION (or, in edge case, cookies)
If you select to use SESSION, you can just assign a session variable to true
$_SESSION['registered'] = true;
and checking it on the first page
if (isset($_SESSION['registered'])) {
unset($_SESSION['registered']);
// shot the message
}
Typically you would set your flag for success in the session to display this message when the next page loads. This is commonly referred to as a Flash Message. You would then check the value/existence of this session flag and show your message or not accordingly. In most frameworks there is built in functionality for this which includes the clean up of the flag on the next request so that the message is only displayed directly after the action generating it is taken.
From the CI Sessions Documentation:
CodeIgniter supports "flashdata", or session data that will only be
available for the next server request, and are then automatically
cleared. These can be very useful, and are typically used for
informational or status messages (for example: "record 2 deleted").
Note: Flash variables are prefaced with "flash_" so avoid this prefix
in your own session names.
To add flashdata:
$this->session->set_flashdata('item', 'value');
You can also pass an array to set_flashdata(), in the same manner as
set_userdata().
To read a flashdata variable:
$this->session->flashdata('item');
If you find that you need to preserve a flashdata variable through an
additional request, you can do so using the keep_flashdata() function.
$this->session->keep_flashdata('item');
You should have some verification checks in your code that handles the processing of the form data to make sure that the required fields are filled out. Otherwise, you should be redirecting to your first page to have the user fill out the form.
Also, this could be handled via AJAX, but that would be a second step to having the proper verification in your form-processing page
HTML:
<form method="post">
<input type="text">
<input name="submitted" type="submit">
</form>
PHP:
if($_POST['submitted']{
//post was submitted process it
if(/*whatever you're doing to the form succeeds*/){
//show success
}
}
POST will not show variables in the URL.
Several solutions here, one would be to check for the form submission and if it hasn't been submitted redirect to the page with the form on it.
ie:
<?php
if (isset($_POST['submit']))
{
// process the form
}
else
{
//redirect to the form itself
header( 'Location: http://www.yourform.com' ) ;
}
?>

How to prevent the "Confirm Form Resubmission" dialog?

How do I clean information in a form after submit so that it does not show this error after a page refresh?
See image (from chrome):
The dialog has the text:
The page that you're looking for used
information that you entered. Returning to that
page might cause any action you took to be
repeated. Do you want to continue?
I want this dialog not to appear.
This method works for me well and I think the simplest way to do this is to use this javascript code inside the reloaded page's HTML.
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
Edit: It's been a few years since I originally posted this answer, and even though I got a few upvotes, I'm not really happy with my previous answer, so I have redone it completely. I hope this helps.
When to use GET and POST:
One way to get rid of this error message is to make your form use GET instead of POST. Just keep in mind that this is not always an appropriate solution (read below).
Always use POST if you are performing an action that you don't want to be repeated, if sensitive information is being transferred or if your form contains either a file upload or the length of all data sent is longer than ~2000 characters.
Examples of when to use POST would include:
A login form
A contact form
A submit payment form
Something that adds, edits or deletes entries from a database
An image uploader (note, if using GET with an <input type="file"> field, only the filename will be sent to the server, which 99.73% of the time is not what you want.)
A form with many fields (which would create a long URL if using GET)
In any of these cases, you don't want people refreshing the page and re-sending the data. If you are sending sensitive information, using GET would not only be inappropriate, it would be a security issue (even if the form is sent by AJAX) since the sensitive item (e.g. user's password) is sent in the URL and will therefore show up in server access logs.
Use GET for basically anything else. This means, when you don't mind if it is repeated, for anything that you could provide a direct link to, when no sensitive information is being transferred, when you are pretty sure your URL lengths are not going to get out of control and when your forms don't have any file uploads.
Examples would include:
Performing a search in a search engine
A navigation form for navigating around the website
Performing one-time actions using a nonce or single use password (such as an "unsubscribe" link in an email).
In these cases POST would be completely inappropriate. Imagine if search engines used POST for their searches. You would receive this message every time you refreshed the page and you wouldn't be able to just copy and paste the results URL to people, they would have to manually fill out the form themselves.
If you use POST:
To me, in most cases even having the "Confirm form resubmission" dialog pop up shows that there is a design flaw. By the very nature of POST being used to perform destructive actions, web designers should prevent users from ever performing them more than once by accidentally (or intentionally) refreshing the page. Many users do not even know what this dialog means and will therefore just click on "Continue". What if that was after a "submit payment" request? Does the payment get sent again?
So what do you do? Fortunately we have the Post/Redirect/Get design pattern. The user submits a POST request to the server, the server redirects the user's browser to another page and that page is then retrieved using GET.
Here is a simple example using PHP:
if(!empty($_POST['username'] && !empty($_POST['password'])) {
$user = new User;
$user->login($_POST['username'], $_POST['password']);
if ($user->isLoggedIn()) {
header("Location: /admin/welcome.php");
exit;
}
else {
header("Location: /login.php?invalid_login");
}
}
Notice how in this example even when the password is incorrect, I am still redirecting back to the login form. To display an invalid login message to the user, just do something like:
if (isset($_GET['invalid_login'])) {
echo "Your username and password combination is invalid";
}
It has nothing to do with your form or the values in it. It gets fired by the browser to prevent the user from repeating the same request with the cached data. If you really need to enable the refreshing of the result page, you should redirect the user, either via PHP (header('Location:result.php');) or other server-side language you're using. Meta tag solution should work also to disable the resending on refresh.
After processing the POST page, redirect the user to the same page.
On
http://test.com/test.php
header('Location: http://test.com/test.php');
This will get rid of the box, as refreshing the page will not resubmit the data.
It seems you are looking for the Post/Redirect/Get pattern.
As another solution you may stop to use redirecting at all.
You may process and render the processing result at once with no POST confirmation alert. You should just manipulate the browser history object:
history.replaceState("", "", "/the/result/page")
See full or short answers
You could try using AJAX calls with jQuery. Like how youtube adds your comment without refreshing. This would remove the problem with refreshing overal.
You'd need to send the info necessary trough the ajax call.
I'll use the youtube comment as example.
$.ajax({
type: 'POST',
url: 'ajax/comment-on-video.php',
data: {
comment: $('#idOfInputField').val();
},
success: function(obj) {
if(obj === 'true') {
//Some code that recreates the inserted comment on the page.
}
}
});
You can now create the file comment-on-video.php and create something like this:
<?php
session_start();
if(isset($_POST['comment'])) {
$comment = mysqli_real_escape_string($db, $_POST['comment']);
//Given you are logged in and store the user id in the session.
$user = $_SESSION['user_id'];
$query = "INSERT INTO `comments` (`comment_text`, `user_id`) VALUES ($comment, $user);";
$result = mysqli_query($db, $query);
if($result) {
echo true;
exit();
}
}
echo false;
exit();
?>
I had a situation where I could not use any of the above answers. My case involved working with search page where users would get "confirm form resubmission" if the clicked back after navigating to any of the search results. I wrote the following javascript which worked around the issue. It isn't a great fix as it is a bit blinky, and it doesn't work on IE8 or earlier. Still, though this might be useful or interesting for someone dealing with this issue.
jQuery(document).ready(function () {
//feature test
if (!history)
return;
var searchBox = jQuery("#searchfield");
//This occurs when the user get here using the back button
if (history.state && history.state.searchTerm != null && history.state.searchTerm != "" && history.state.loaded != null && history.state.loaded == 0) {
searchBox.val(history.state.searchTerm);
//don't chain reloads
history.replaceState({ searchTerm: history.state.searchTerm, page: history.state.page, loaded: 1 }, "", document.URL);
//perform POST
document.getElementById("myForm").submit();
return;
}
//This occurs the first time the user hits this page.
history.replaceState({ searchTerm: searchBox.val(), page: pageNumber, loaded: 0 }, "", document.URL);
});
I found an unorthodox way to accomplish this.
Just put the script page in an iframe. Doing so allows the page to be refreshed, seemingly even on older browsers without the "confirm form resubmission" message ever appearing.
Quick Answer
Use different methods to load the form and save/process form.
Example.
Login.php
Load login form at Login/index
Validate login at Login/validate
On Success
Redirect the user to User/dashboard
On failure
Redirect the user to login/index

Categories