There are a few options.
Use vectors
During each loop, it's expensive to do df$x because it takes memory to do it. Instead, you can pre-assign vectors and subset the vectors.
#easiest - extract the vectors before the loop
C <- df[['c']] #used big C because c() is a function
a <- df[['a']]
b <- df[['b']]
x <- df[['x']]
for(i in seq_along(x)[-1]) x[i] <- C[i] * a[i-1] / x[i-1L] + b[i] * a[i]
Use a function
Turning your loop into a function will improve performance due to the optimization from compiling.
f_recurse = function(a, b, C, x){
  for (i in seq_along(x)[-1]) x[i] <- C[i] * a[i-1] / x[i-1L] + b[i] * a[i]
  x
}
f_recurse(df$a, df$b, df$c, df$x)
Use Rcpp
Finally, if the response is still too laggy, you can try to use Rcpp. Note, Rcpp updates in place so while I return a vector, there's really no need - the df$x has also been updated.
library(Rcpp)
cppFunction('
NumericVector f_recurse_rcpp(IntegerVector a, IntegerVector b, NumericVector C, NumericVector x){
for (int i = 1; i < x.size(); ++i){
 x[i] = C[i] * a[i-1] / x[i - 1] + b[i] * a[i];
}
return(x);
}
')
f_recurse_rcpp(df$a, df$b, df$c, df$x)
Performance
In all, we get close to a 1,000 times performance increase. The table below is from bench::mark which also checks for equality.
# A tibble: 4 x 13
  expression                                 min  median `itr/sec` mem_alloc
  <bch:expr>                             <bch:t> <bch:t>     <dbl> <bch:byt>
1 OP                                      8.27ms   8.8ms      106.   62.04KB
2 extract                                 6.21ms  7.49ms      126.   46.16KB
3 f_recurse(df$a, df$b, df$c, df$x)       13.1us  28.8us    33295.        0B
4 f_recurse_rcpp(df$a, df$b, df$c, df$x)   8.6us    10us    98240.    2.49KB
And here's an example with a 1,000 row data.frame and then 10,000 row
df <- data.frame(a = sample(1000L),
                 b = sample(1001:2000),
                 c = seq(1000, 11000, length.out = 1000),
                 x = rep(3, 1000L))
# A tibble: 4 x 13
  expression                                 min   median `itr/sec` mem_alloc
  <bch:expr>                             <bch:t> <bch:tm>     <dbl> <bch:byt>
1 OP                                      23.9ms  24.38ms      39.4    7.73MB
2 extract                                  6.5ms   7.71ms     123.    69.84KB
3 f_recurse(df$a, df$b, df$c, df$x)      265.7us  271.9us    3596.    23.68KB
4 f_recurse_rcpp(df$a, df$b, df$c, df$x)  17.4us   18.9us   51845.     2.49KB
df <- data.frame(a = sample(10000L),
                 b = sample(10001:20000),
                 c = seq(1000, 11000, length.out = 10000),
                 x = rep(3, 10000L))
# A tibble: 4 x 13
  expression                                  min   median `itr/sec` mem_alloc
  <bch:expr>                             <bch:tm> <bch:tm>     <dbl> <bch:byt>
1 OP                                     353.17ms 412.62ms      2.42  763.38MB
2 extract                                  8.75ms   8.95ms    107.    280.77KB
3 f_recurse(df$a, df$b, df$c, df$x)        2.58ms   2.61ms    376.    234.62KB
4 f_recurse_rcpp(df$a, df$b, df$c, df$x)   98.6us  112.7us   8169.      2.49KB