Usando sesiones.
Voy a escribir (de memoria) un ejemplo sencillo. Supondré que usas dos archivos, uno para el formulario, otro para procesar los datos de ese formulario. Me limito solo al código relevante.
------ formulario.php ---------
<?php
session_start();
$err = FALSE;
if (isset($_SESSION["form1"])) { // si la sesión existe...
$err = TRUE; // entonces hubo error.
}
$valor1 = @$_SESSION["form1"]["valor1"]; // (1)
$valor2 = @$_SESSION["form1"]["valor2"]; // (1)
?>
...
<form name="form1" id="form1" method="POST" action="proceso.php">
<input name="valor1" type="text" id="valor1" value="<?=$valor1?>">
<?php if ($err) { // hubo error
if (@$_SESSION["form1"]["valor1"] == "") { // y es en este campo
?>
<br><font size="small" color="#FF0000">Este campo no puede estar vacío</font>
<?php } } ?>
<input name="valor2" type="text" id="valor2" value="<?=$valor2?>">
<?php if ($err) { // hubo error
if (@$_SESSION["form1"]["valor2"] == "") { // y es en este campo
?>
<br><font size="small" color="#FF0000">Este campo no puede estar vacío</font>
<?php } } ?>
-------------------
-------- proceso.php ----------
<?php
session_start();
$_SESSION["form1"] = array(); // (2)
$valor1 = @$_POST["valor1"];
$_SESSION["form1"]["valor1"] = $valor1;
$valor2 = @$_POST["valor2"];
$_SESSION["form1"]["valor2"] = $valor2;
if (($valor1 == "") or ($valor2 == "")) { // si alguno o los dos campos están vacíos...
header( "Location: http://www.example.com/formulario.php"); // regresar al formulario
exit; // y terminar el script.
}
/* si llegó hasta aquí, ambos campos están llenos, entonces procesarlos */
unset($_SESSION["form1"]); // destruyo el array.
?>
---------------
Notas:
(1) El @ frente a la variable de sesión hace que en caso de que no esté definida asigne vacío (nulo) a la variable y no cause un error.
(2) Crea un array en esa variable de sesión.
La idea general es usar variables de sesión para trasladar los datos desde proceso.php a formulario.php en caso de que proceso.php determine que algún campo del formulario está vacío.
En condiciones normales cuando el usuario carga formulario.php por primera vez el script verá que $_SESSION["form1"] no existe por lo tanto $valor1 y $valor2 estarán vacíos y eso es lo que se mostrará en el formulario HTML.
El usuario llena (o no) ambos inputs y presiona "enviar".
Los datos llegan por POST a proceso.php.
Se crea la variable de sesion $_SESSION["form1"] vacía.
Se le asignan los datos llegados por POST desde el formulario.
Se verifica que los datos no estén vaciós.
Si no lo están el script continua.
Pero si alguno o ambos campos el usuario los dejó vacíos el script fuerza al navegador a regresar a formulario.php.
Es aquí donde formulario.php ve que $_SESSION["form1"] sí existe ya que se creó en proceso.php, entonces $valor1 y $valor2 en formulario.php toman los valores almacenados en la variable de sesión y los reproduce en los campos de formulario HTML apropiado y con un mensaje informandole al usuario lo que ha ocurrido.
Es importante que destruyas $_SESSION["form1"] al final de proceso.php si todo ha ido bien porque si el usuario vuelve a formulario.php por alguna razón, éste verá que aún existe esa variable de sesión y asumirá que hubo un error cuando no es así.
Saludos...